diff options
author | Yang Ni <yangni@google.com> | 2015-04-17 16:51:55 -0700 |
---|---|---|
committer | Yang Ni <yangni@google.com> | 2015-04-21 14:13:47 -0700 |
commit | 18314caccd4e4b7cf59f2a18a38181f8e3d88634 (patch) | |
tree | 97dbae15b3ecd991e969a4dbd1e460dcc89b2916 /rs | |
parent | 9d4efdf2802f06ccf7031610891f75af70ea5538 (diff) | |
download | frameworks_base-18314caccd4e4b7cf59f2a18a38181f8e3d88634.zip frameworks_base-18314caccd4e4b7cf59f2a18a38181f8e3d88634.tar.gz frameworks_base-18314caccd4e4b7cf59f2a18a38181f8e3d88634.tar.bz2 |
Move new script group API into ScriptGroup class
And mark existing methods and builder deprecated.
Removed ScriptGroup2 class.
Renamed nested class UnboundValue to Input.
Marked classes Closure, Future, and Input as final.
Change-Id: I362910c115e9d0173dc515674e08bf4878232879
Diffstat (limited to 'rs')
-rw-r--r-- | rs/java/android/renderscript/ScriptGroup.java | 597 | ||||
-rw-r--r-- | rs/java/android/renderscript/ScriptGroup2.java | 592 |
2 files changed, 577 insertions, 612 deletions
diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java index 51c838f..be8b0fd 100644 --- a/rs/java/android/renderscript/ScriptGroup.java +++ b/rs/java/android/renderscript/ScriptGroup.java @@ -16,32 +16,30 @@ package android.renderscript; +import android.util.Log; +import android.util.Pair; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** - * ScriptGroup creates a group of kernels that are executed - * together with one execution call as if they were a single kernel. - * The kernels may be connected internally or to an external allocation. - * The intermediate results for internal connections are not observable - * after the execution of the script. + * A group of kernels that are executed + * together with one execution call as if they were a single kernel * <p> - * External connections are grouped into inputs and outputs. - * All outputs are produced by a script kernel and placed into a - * user-supplied allocation. Inputs provide the input of a kernel. - * Inputs bound to script globals are set directly upon the script. + * In addition to kernels, a script group may contain invocable functions as well. + * A script group may take inputs and generate outputs, which are consumed and + * produced by its member kernels. + * Inside a script group, outputs from one kernel can be passed to another kernel as inputs. + * The API disallows cyclic dependencies among kernels in a script group, + * effectively making it a directed acyclic graph (DAG) of kernels. * <p> - * A ScriptGroup must contain at least one kernel. A ScriptGroup - * must contain only a single directed acyclic graph (DAG) of - * script kernels and connections. Attempting to create a - * ScriptGroup with multiple DAGs or attempting to create - * a cycle within a ScriptGroup will throw an exception. - * <p> - * Currently, all kernels in a ScriptGroup must be from separate - * Script objects. Attempting to use multiple kernels from the same - * Script object will result in an {@link android.renderscript.RSInvalidStateException}. - * + * Grouping kernels together allows for more efficient execution. For example, + * runtime and compiler optimization can be applied to reduce computation and + * communication overhead, and to make better use of the CPU and the GPU. **/ public final class ScriptGroup extends BaseObj { + private static final String TAG = "ScriptGroup"; IO mOutputs[]; IO mInputs[]; @@ -88,15 +86,364 @@ public final class ScriptGroup extends BaseObj { } + /** + * An opaque class for closures + * <p> + * A closure represents a function call to a kernel or invocable function, + * combined with arguments and values for global variables. A closure is + * created using the {@link android.renderscript.ScriptGroup.Builder2#addKernel} or + * {@link android.renderscript.ScriptGroup.Builder2#addInvoke} + * method. + */ + + public static final class Closure extends BaseObj { + private Object[] mArgs; + private Allocation mReturnValue; + private Map<Script.FieldID, Object> mBindings; + + private Future mReturnFuture; + private Map<Script.FieldID, Future> mGlobalFuture; + + private FieldPacker mFP; + + private static final String TAG = "Closure"; + + Closure(long id, RenderScript rs) { + super(id, rs); + } + + Closure(RenderScript rs, Script.KernelID kernelID, Type returnType, + Object[] args, Map<Script.FieldID, Object> globals) { + super(0, rs); + + mArgs = args; + mReturnValue = Allocation.createTyped(rs, returnType); + mBindings = globals; + mGlobalFuture = new HashMap<Script.FieldID, Future>(); + + int numValues = args.length + globals.size(); + + long[] fieldIDs = new long[numValues]; + long[] values = new long[numValues]; + int[] sizes = new int[numValues]; + long[] depClosures = new long[numValues]; + long[] depFieldIDs = new long[numValues]; + + int i; + for (i = 0; i < args.length; i++) { + Object obj = args[i]; + fieldIDs[i] = 0; + if (obj instanceof Input) { + Input unbound = (Input)obj; + unbound.addReference(this, i); + } else { + retrieveValueAndDependenceInfo(rs, i, args[i], values, sizes, + depClosures, depFieldIDs); + } + } + + for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) { + Object obj = entry.getValue(); + Script.FieldID fieldID = entry.getKey(); + fieldIDs[i] = fieldID.getID(rs); + if (obj instanceof Input) { + Input unbound = (Input)obj; + unbound.addReference(this, fieldID); + } else { + retrieveValueAndDependenceInfo(rs, i, obj, values, + sizes, depClosures, depFieldIDs); + } + i++; + } + + long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs), + fieldIDs, values, sizes, depClosures, depFieldIDs); + + setID(id); + } + + Closure(RenderScript rs, Script.InvokeID invokeID, + Object[] args, Map<Script.FieldID, Object> globals) { + super(0, rs); + mFP = FieldPacker.createFromArray(args); + + mArgs = args; + mBindings = globals; + mGlobalFuture = new HashMap<Script.FieldID, Future>(); + + int numValues = globals.size(); + + long[] fieldIDs = new long[numValues]; + long[] values = new long[numValues]; + int[] sizes = new int[numValues]; + long[] depClosures = new long[numValues]; + long[] depFieldIDs = new long[numValues]; + + int i = 0; + for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) { + Object obj = entry.getValue(); + Script.FieldID fieldID = entry.getKey(); + fieldIDs[i] = fieldID.getID(rs); + if (obj instanceof Input) { + Input unbound = (Input)obj; + unbound.addReference(this, fieldID); + } else { + retrieveValueAndDependenceInfo(rs, i, obj, values, + sizes, depClosures, depFieldIDs); + } + i++; + } + + long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs, + values, sizes); + + setID(id); + } + + private static + void retrieveValueAndDependenceInfo(RenderScript rs, + int index, Object obj, + long[] values, int[] sizes, + long[] depClosures, + long[] depFieldIDs) { + + if (obj instanceof Future) { + Future f = (Future)obj; + obj = f.getValue(); + depClosures[index] = f.getClosure().getID(rs); + Script.FieldID fieldID = f.getFieldID(); + depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0; + if (obj == null) { + // Value is originally created by the owner closure + values[index] = 0; + sizes[index] = 0; + return; + } + } else { + depClosures[index] = 0; + depFieldIDs[index] = 0; + } + + ValueAndSize vs = new ValueAndSize(rs, obj); + values[index] = vs.value; + sizes[index] = vs.size; + } + + /** + * Returns the future for the return value + * + * @return a future + */ + + public Future getReturn() { + if (mReturnFuture == null) { + mReturnFuture = new Future(this, null, mReturnValue); + } + + return mReturnFuture; + } + + /** + * Returns the future for a global variable + * + * @param field the field ID for the global variable + * @return a future + */ + + public Future getGlobal(Script.FieldID field) { + Future f = mGlobalFuture.get(field); + + if (f == null) { + // If the field is not bound to this closure, this will return a future + // without an associated value (reference). So this is not working for + // cross-module (cross-script) linking in this case where a field not + // explicitly bound. + f = new Future(this, field, mBindings.get(field)); + mGlobalFuture.put(field, f); + } + + return f; + } + + void setArg(int index, Object obj) { + mArgs[index] = obj; + ValueAndSize vs = new ValueAndSize(mRS, obj); + mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size); + } + + void setGlobal(Script.FieldID fieldID, Object obj) { + mBindings.put(fieldID, obj); + ValueAndSize vs = new ValueAndSize(mRS, obj); + mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size); + } + + private static final class ValueAndSize { + public ValueAndSize(RenderScript rs, Object obj) { + if (obj instanceof Allocation) { + value = ((Allocation)obj).getID(rs); + size = -1; + } else if (obj instanceof Boolean) { + value = ((Boolean)obj).booleanValue() ? 1 : 0; + size = 4; + } else if (obj instanceof Integer) { + value = ((Integer)obj).longValue(); + size = 4; + } else if (obj instanceof Long) { + value = ((Long)obj).longValue(); + size = 8; + } else if (obj instanceof Float) { + value = ((Float)obj).longValue(); + size = 4; + } else if (obj instanceof Double) { + value = ((Double)obj).longValue(); + size = 8; + } + } + public long value; + public int size; + } + } + + /** + * An opaque class for futures + * <p> + * A future represents an output of a closure, either the return value of + * the function, or the value of a global variable written by the function. + * A future is created by calling the {@link Closure#getReturn} or + * {@link Closure#getGlobal} method. + */ + + public static final class Future { + Closure mClosure; + Script.FieldID mFieldID; + Object mValue; + + Future(Closure closure, Script.FieldID fieldID, Object value) { + mClosure = closure; + mFieldID = fieldID; + mValue = value; + } + + Closure getClosure() { return mClosure; } + Script.FieldID getFieldID() { return mFieldID; } + Object getValue() { return mValue; } + } + + /** + * An opaque class for script group inputs + * <p> + * Created by calling the {@link Builder2#addInput} method. The value + * is assigned in {@link ScriptGroup#execute(Object...)} method as + * one of its arguments. Arguments to the execute method should be in + * the same order as intputs are added using the addInput method. + */ + + public static final class Input { + // Either mFieldID or mArgIndex should be set but not both. + List<Pair<Closure, Script.FieldID>> mFieldID; + // -1 means unset. Legal values are 0 .. n-1, where n is the number of + // arguments for the referencing closure. + List<Pair<Closure, Integer>> mArgIndex; + + Input() { + mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>(); + mArgIndex = new ArrayList<Pair<Closure, Integer>>(); + } + + void addReference(Closure closure, int index) { + mArgIndex.add(Pair.create(closure, Integer.valueOf(index))); + } + + void addReference(Closure closure, Script.FieldID fieldID) { + mFieldID.add(Pair.create(closure, fieldID)); + } + + void set(Object value) { + for (Pair<Closure, Integer> p : mArgIndex) { + Closure closure = p.first; + int index = p.second.intValue(); + closure.setArg(index, value); + } + for (Pair<Closure, Script.FieldID> p : mFieldID) { + Closure closure = p.first; + Script.FieldID fieldID = p.second; + closure.setGlobal(fieldID, value); + } + } + } + + private String mName; + private List<Closure> mClosures; + private List<Input> mInputs2; + private Future[] mOutputs2; + ScriptGroup(long id, RenderScript rs) { super(id, rs); } + ScriptGroup(RenderScript rs, String name, List<Closure> closures, + List<Input> inputs, Future[] outputs) { + super(0, rs); + mName = name; + mClosures = closures; + mInputs2 = inputs; + mOutputs2 = outputs; + + long[] closureIDs = new long[closures.size()]; + for (int i = 0; i < closureIDs.length; i++) { + closureIDs[i] = closures.get(i).getID(rs); + } + long id = rs.nScriptGroup2Create(name, ScriptC.mCachePath, closureIDs); + setID(id); + } + + /** + * Executes a script group + * + * @param inputs inputs to the script group + * @return outputs of the script group as an array of objects + */ + + public Object[] execute(Object... inputs) { + if (inputs.length < mInputs2.size()) { + Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " + + "less than expected " + mInputs2.size()); + return null; + } + + if (inputs.length > mInputs2.size()) { + Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " + + "more than expected " + mInputs2.size()); + } + + for (int i = 0; i < mInputs2.size(); i++) { + Object obj = inputs[i]; + if (obj instanceof Future || obj instanceof Input) { + Log.e(TAG, this.toString() + ": input " + i + + " is a future or unbound value"); + return null; + } + Input unbound = mInputs2.get(i); + unbound.set(obj); + } + + mRS.nScriptGroup2Execute(getID(mRS)); + + Object[] outputObjs = new Object[mOutputs2.length]; + int i = 0; + for (Future f : mOutputs2) { + outputObjs[i++] = f.getValue(); + } + return outputObjs; + } + /** * Sets an input of the ScriptGroup. This specifies an * Allocation to be used for kernels that require an input * Allocation provided from outside of the ScriptGroup. * + * @deprecated Set arguments to {@link #execute(Object...)} instead. + * * @param s The ID of the kernel where the allocation should be * connected. * @param a The allocation to connect. @@ -117,6 +464,8 @@ public final class ScriptGroup extends BaseObj { * Allocation to be used for the kernels that require an output * Allocation visible after the ScriptGroup is executed. * + * @deprecated Use return value of {@link #execute(Object...)} instead. + * * @param s The ID of the kernel where the allocation should be * connected. * @param a The allocation to connect. @@ -136,6 +485,9 @@ public final class ScriptGroup extends BaseObj { * Execute the ScriptGroup. This will run all the kernels in * the ScriptGroup. No internal connection results will be visible * after execution of the ScriptGroup. + * + * @deprecated Use {@link #execute} instead. + * */ public void execute() { mRS.nScriptGroupExecute(getID(mRS)); @@ -164,6 +516,8 @@ public final class ScriptGroup extends BaseObj { * Once all connections are made, a call to {@link #create} will * return the ScriptGroup object. * + * @deprecated Use {@link Builder2} instead. + * */ public static final class Builder { private RenderScript mRS; @@ -464,7 +818,210 @@ public final class ScriptGroup extends BaseObj { } + /** + * Represents a binding of a value to a global variable in a + * kernel or invocable function. Used in closure creation. + */ -} + public static final class Binding { + private final Script.FieldID mField; + private final Object mValue; + + /** + * Returns a Binding object that binds value to field + * + * @param field the Script.FieldID of the global variable + * @param value the value + */ + + public Binding(Script.FieldID field, Object value) { + mField = field; + mValue = value; + } + + /** + * Returns the field ID + */ + + public Script.FieldID getField() { return mField; } + + /** + * Returns the value + */ + + public Object getValue() { return mValue; } + } + + /** + * The builder class for creating script groups + * <p> + * A script group is created using closures (see class {@link Closure}). + * A closure is a function call to a kernel or + * invocable function. Each function argument or global variable accessed inside + * the function is bound to 1) a known value, 2) a script group input + * (see class {@link Input}), or 3) a + * future (see class {@link Future}). + * A future is the output of a closure, either the return value of the + * function or a global variable written by that function. + * <p> + * Closures are created using the {@link #addKernel} or {@link #addInvoke} + * methods. + * When a closure is created, futures from previously created closures + * can be used as its inputs. + * External script group inputs can be used as inputs to individual closures as well. + * An external script group input is created using the {@link #addInput} method. + * A script group is created by a call to the {@link #create} method, which + * accepts an array of futures as the outputs for the script group. + * <p> + * Closures in a script group can be evaluated in any order as long as the + * following conditions are met: + * 1) a closure must be evaluated before any other closures that take its + * futures as inputs; + * 2) all closures added before an invoke closure must be evaluated + * before it; + * and 3) all closures added after an invoke closure must be evaluated after + * it. + * As a special case, the order that the closures are added is a legal + * evaluation order. However, other evaluation orders are possible, including + * concurrently evaluating independent closures. + */ + + public static final class Builder2 { + RenderScript mRS; + List<Closure> mClosures; + List<Input> mInputs; + private static final String TAG = "ScriptGroup.Builder2"; + + /** + * Returns a Builder object + * + * @param rs the RenderScript context + */ + public Builder2(RenderScript rs) { + mRS = rs; + mClosures = new ArrayList<Closure>(); + mInputs = new ArrayList<Input>(); + } + /** + * Adds a closure for a kernel + * + * @param k Kernel ID for the kernel function + * @param returnType Allocation type for the return value + * @param args arguments to the kernel function + * @param globalBindings bindings for global variables + * @return a closure + */ + private Closure addKernelInternal(Script.KernelID k, Type returnType, Object[] args, + Map<Script.FieldID, Object> globalBindings) { + Closure c = new Closure(mRS, k, returnType, args, globalBindings); + mClosures.add(c); + return c; + } + + /** + * Adds a closure for an invocable function + * + * @param invoke Invoke ID for the invocable function + * @param args arguments to the invocable function + * @param globalBindings bindings for global variables + * @return a closure + */ + + private Closure addInvokeInternal(Script.InvokeID invoke, Object[] args, + Map<Script.FieldID, Object> globalBindings) { + Closure c = new Closure(mRS, invoke, args, globalBindings); + mClosures.add(c); + return c; + } + + /** + * Adds a script group input + * + * @return a script group input, which can be used as an argument or a value to + * a global variable for creating closures + */ + public Input addInput() { + Input unbound = new Input(); + mInputs.add(unbound); + return unbound; + } + + /** + * Adds a closure for a kernel + * + * @param k Kernel ID for the kernel function + * @param argsAndBindings arguments followed by bindings for global variables + * @return a closure + */ + + public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) { + ArrayList<Object> args = new ArrayList<Object>(); + Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>(); + if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) { + return null; + } + return addKernelInternal(k, returnType, args.toArray(), bindingMap); + } + + /** + * Adds a closure for an invocable function + * + * @param invoke Invoke ID for the invocable function + * @param argsAndBindings arguments followed by bindings for global variables + * @return a closure + */ + + public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) { + ArrayList<Object> args = new ArrayList<Object>(); + Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>(); + if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) { + return null; + } + return addInvokeInternal(invoke, args.toArray(), bindingMap); + } + + /** + * Creates a script group + * + * @param name name for the script group. Legal names can only contain letters, digits, + * '-', or '_'. The name can be no longer than 100 characters. + * @param outputs futures intended as outputs of the script group + * @return a script group + */ + + public ScriptGroup create(String name, Future... outputs) { + if (name == null || name.isEmpty() || name.length() > 100 || + !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) { + throw new RSIllegalArgumentException("invalid script group name"); + } + ScriptGroup ret = new ScriptGroup(mRS, name, mClosures, mInputs, outputs); + return ret; + } + + private boolean seperateArgsAndBindings(Object[] argsAndBindings, + ArrayList<Object> args, + Map<Script.FieldID, Object> bindingMap) { + int i; + for (i = 0; i < argsAndBindings.length; i++) { + if (argsAndBindings[i] instanceof Binding) { + break; + } + args.add(argsAndBindings[i]); + } + + for (; i < argsAndBindings.length; i++) { + if (!(argsAndBindings[i] instanceof Binding)) { + return false; + } + Binding b = (Binding)argsAndBindings[i]; + bindingMap.put(b.getField(), b.getValue()); + } + + return true; + } + + } + +} diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java deleted file mode 100644 index 96bb6e2..0000000 --- a/rs/java/android/renderscript/ScriptGroup2.java +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.renderscript; - -import android.util.Log; -import android.util.Pair; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * ScriptGroup2 is a new, enhanced API for script groups. - * A script group is a collection of kernels or invocable functions, with - * data dependencies defined among them. A script group is launched for - * execution as a whole, rather than launching each kernel or invocable function - * separately. Once created, a script group can be repeatedly used with - * different inputs. - * <p> - * In the new ScriptGroup2 API, a script group is modeled using closures. - * A closure, in this context, is defined as a function call to a kernel or - * invocable function. Each function argument or global variable accessed inside - * the function is bound to 1) a known value, 2) a script group input, or 3) a - * future. A future is the output of a closure, i.e., the return value of the - * function or a global variable written by that function. - * <p> - * A script group is a directed acyclic graph (DAG), in which closures are the - * vertices and the dependencies among them are the edges. - * The way the ScriptGroup2 API is designed makes cycles impossible in a script - * group. For example, it is impossible to make forward references to futures, - * i.e., it is impossible to set as input to a closure the future from itself or - * a future from another closure that directly or indirectly depends on it. - * <p> - * Grouping kernels and invocable functions together allows to execute them more - * efficiently. Runtime and compiler optimizations are applied to script - * groups, to reduce computation or communication overhead, and to make more - * efficient use of the CPU and the GPU. - */ - -public class ScriptGroup2 extends BaseObj { - - /** - * An opaque class for closures - */ - - public static class Closure extends BaseObj { - private Object[] mArgs; - private Allocation mReturnValue; - private Map<Script.FieldID, Object> mBindings; - - private Future mReturnFuture; - private Map<Script.FieldID, Future> mGlobalFuture; - - private FieldPacker mFP; - - private static final String TAG = "Closure"; - - public Closure(long id, RenderScript rs) { - super(id, rs); - } - - public Closure(RenderScript rs, Script.KernelID kernelID, Type returnType, - Object[] args, Map<Script.FieldID, Object> globals) { - super(0, rs); - - mArgs = args; - mReturnValue = Allocation.createTyped(rs, returnType); - mBindings = globals; - mGlobalFuture = new HashMap<Script.FieldID, Future>(); - - int numValues = args.length + globals.size(); - - long[] fieldIDs = new long[numValues]; - long[] values = new long[numValues]; - int[] sizes = new int[numValues]; - long[] depClosures = new long[numValues]; - long[] depFieldIDs = new long[numValues]; - - int i; - for (i = 0; i < args.length; i++) { - Object obj = args[i]; - fieldIDs[i] = 0; - if (obj instanceof UnboundValue) { - UnboundValue unbound = (UnboundValue)obj; - unbound.addReference(this, i); - } else { - retrieveValueAndDependenceInfo(rs, i, args[i], values, sizes, - depClosures, depFieldIDs); - } - } - - for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) { - Object obj = entry.getValue(); - Script.FieldID fieldID = entry.getKey(); - fieldIDs[i] = fieldID.getID(rs); - if (obj instanceof UnboundValue) { - UnboundValue unbound = (UnboundValue)obj; - unbound.addReference(this, fieldID); - } else { - retrieveValueAndDependenceInfo(rs, i, obj, values, - sizes, depClosures, depFieldIDs); - } - i++; - } - - long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs), - fieldIDs, values, sizes, depClosures, depFieldIDs); - - setID(id); - } - - public Closure(RenderScript rs, Script.InvokeID invokeID, - Object[] args, Map<Script.FieldID, Object> globals) { - super(0, rs); - mFP = FieldPacker.createFromArray(args); - - mArgs = args; - mBindings = globals; - mGlobalFuture = new HashMap<Script.FieldID, Future>(); - - int numValues = globals.size(); - - long[] fieldIDs = new long[numValues]; - long[] values = new long[numValues]; - int[] sizes = new int[numValues]; - long[] depClosures = new long[numValues]; - long[] depFieldIDs = new long[numValues]; - - int i = 0; - for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) { - Object obj = entry.getValue(); - Script.FieldID fieldID = entry.getKey(); - fieldIDs[i] = fieldID.getID(rs); - if (obj instanceof UnboundValue) { - UnboundValue unbound = (UnboundValue)obj; - unbound.addReference(this, fieldID); - } else { - retrieveValueAndDependenceInfo(rs, i, obj, values, - sizes, depClosures, depFieldIDs); - } - i++; - } - - long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs, - values, sizes); - - setID(id); - } - - private static - void retrieveValueAndDependenceInfo(RenderScript rs, - int index, Object obj, - long[] values, int[] sizes, - long[] depClosures, - long[] depFieldIDs) { - - if (obj instanceof Future) { - Future f = (Future)obj; - obj = f.getValue(); - depClosures[index] = f.getClosure().getID(rs); - Script.FieldID fieldID = f.getFieldID(); - depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0; - if (obj == null) { - // Value is originally created by the owner closure - values[index] = 0; - sizes[index] = 0; - return; - } - } else { - depClosures[index] = 0; - depFieldIDs[index] = 0; - } - - ValueAndSize vs = new ValueAndSize(rs, obj); - values[index] = vs.value; - sizes[index] = vs.size; - } - - /** - * Returns the future for the return value - * - * @return a future - */ - - public Future getReturn() { - if (mReturnFuture == null) { - mReturnFuture = new Future(this, null, mReturnValue); - } - - return mReturnFuture; - } - - /** - * Returns the future for a global variable - * - * @param field the field ID for the global variable - * @return a future - */ - - public Future getGlobal(Script.FieldID field) { - Future f = mGlobalFuture.get(field); - - if (f == null) { - // If the field is not bound to this closure, this will return a future - // without an associated value (reference). So this is not working for - // cross-module (cross-script) linking in this case where a field not - // explicitly bound. - f = new Future(this, field, mBindings.get(field)); - mGlobalFuture.put(field, f); - } - - return f; - } - - void setArg(int index, Object obj) { - mArgs[index] = obj; - ValueAndSize vs = new ValueAndSize(mRS, obj); - mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size); - } - - void setGlobal(Script.FieldID fieldID, Object obj) { - mBindings.put(fieldID, obj); - ValueAndSize vs = new ValueAndSize(mRS, obj); - mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size); - } - - private static final class ValueAndSize { - public ValueAndSize(RenderScript rs, Object obj) { - if (obj instanceof Allocation) { - value = ((Allocation)obj).getID(rs); - size = -1; - } else if (obj instanceof Boolean) { - value = ((Boolean)obj).booleanValue() ? 1 : 0; - size = 4; - } else if (obj instanceof Integer) { - value = ((Integer)obj).longValue(); - size = 4; - } else if (obj instanceof Long) { - value = ((Long)obj).longValue(); - size = 8; - } else if (obj instanceof Float) { - value = ((Float)obj).longValue(); - size = 4; - } else if (obj instanceof Double) { - value = ((Double)obj).longValue(); - size = 8; - } - } - public long value; - public int size; - } - } - - /** - * An opaque class for futures - */ - - public static class Future { - Closure mClosure; - Script.FieldID mFieldID; - Object mValue; - - Future(Closure closure, Script.FieldID fieldID, Object value) { - mClosure = closure; - mFieldID = fieldID; - mValue = value; - } - - Closure getClosure() { return mClosure; } - Script.FieldID getFieldID() { return mFieldID; } - Object getValue() { return mValue; } - } - - /** - * An opaque class for unbound values (a.k.a. script group inputs) - */ - - public static class UnboundValue { - // Either mFieldID or mArgIndex should be set but not both. - List<Pair<Closure, Script.FieldID>> mFieldID; - // -1 means unset. Legal values are 0 .. n-1, where n is the number of - // arguments for the referencing closure. - List<Pair<Closure, Integer>> mArgIndex; - - UnboundValue() { - mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>(); - mArgIndex = new ArrayList<Pair<Closure, Integer>>(); - } - - void addReference(Closure closure, int index) { - mArgIndex.add(Pair.create(closure, Integer.valueOf(index))); - } - - void addReference(Closure closure, Script.FieldID fieldID) { - mFieldID.add(Pair.create(closure, fieldID)); - } - - void set(Object value) { - for (Pair<Closure, Integer> p : mArgIndex) { - Closure closure = p.first; - int index = p.second.intValue(); - closure.setArg(index, value); - } - for (Pair<Closure, Script.FieldID> p : mFieldID) { - Closure closure = p.first; - Script.FieldID fieldID = p.second; - closure.setGlobal(fieldID, value); - } - } - } - - String mName; - List<Closure> mClosures; - List<UnboundValue> mInputs; - Future[] mOutputs; - - private static final String TAG = "ScriptGroup2"; - - public ScriptGroup2(long id, RenderScript rs) { - super(id, rs); - } - - ScriptGroup2(RenderScript rs, String name, List<Closure> closures, - List<UnboundValue> inputs, Future[] outputs) { - super(0, rs); - mName = name; - mClosures = closures; - mInputs = inputs; - mOutputs = outputs; - - long[] closureIDs = new long[closures.size()]; - for (int i = 0; i < closureIDs.length; i++) { - closureIDs[i] = closures.get(i).getID(rs); - } - long id = rs.nScriptGroup2Create(name, ScriptC.mCachePath, closureIDs); - setID(id); - } - - /** - * Executes a script group - * - * @param inputs inputs to the script group - * @return outputs of the script group as an array of objects - */ - - public Object[] execute(Object... inputs) { - if (inputs.length < mInputs.size()) { - Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " + - "less than expected " + mInputs.size()); - return null; - } - - if (inputs.length > mInputs.size()) { - Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " + - "more than expected " + mInputs.size()); - } - - for (int i = 0; i < mInputs.size(); i++) { - Object obj = inputs[i]; - if (obj instanceof Future || obj instanceof UnboundValue) { - Log.e(TAG, this.toString() + ": input " + i + - " is a future or unbound value"); - return null; - } - UnboundValue unbound = mInputs.get(i); - unbound.set(obj); - } - - mRS.nScriptGroup2Execute(getID(mRS)); - - Object[] outputObjs = new Object[mOutputs.length]; - int i = 0; - for (Future f : mOutputs) { - outputObjs[i++] = f.getValue(); - } - return outputObjs; - } - - /** - * A class representing a binding of a value to a global variable in a - * kernel or invocable function. Such a binding can be used to create a - * closure. - */ - - public static final class Binding { - private Script.FieldID mField; - private Object mValue; - - /** - * Returns a Binding object that binds value to field - * - * @param field the Script.FieldID of the global variable - * @param value the value - */ - - public Binding(Script.FieldID field, Object value) { - mField = field; - mValue = value; - } - - /** - * Returns the field ID - */ - - public Script.FieldID getField() { return mField; } - - /** - * Returns the value - */ - - public Object getValue() { return mValue; } - } - - /** - * The builder class to create a script group. - * <p> - * Closures are created using the {@link #addKernel} or {@link #addInvoke} - * methods. - * When a closure is created, futures from previously created closures - * can be used as inputs. - * Unbound values can be used as inputs to create closures as well. - * An unbound value is created using the {@link #addInput} method. - * Unbound values become inputs to the script group to be created, - * in the order that they are added. - * A script group is created by a call to the {@link #create} method, which - * accepts an array of futures as the outputs for the script group. - * <p> - * Closures in a script group can be evaluated in any order as long as the - * following conditions are met. - * First, a closure must be evaluated before any other closures that take its - * futures as inputs. - * Second, all closures added before an invoke closure must be evaluated - * before it. - * Third, all closures added after an invoke closure must be evaluated after - * it. - * <p> - * As a special case, the order that the closures are added is a legal - * evaluation order. However, other evaluation orders are allowed, including - * concurrently evaluating independent closures. - */ - - public static final class Builder { - RenderScript mRS; - List<Closure> mClosures; - List<UnboundValue> mInputs; - private static final String TAG = "ScriptGroup2.Builder"; - - /** - * Returns a Builder object - * - * @param rs the RenderScript context - */ - public Builder(RenderScript rs) { - mRS = rs; - mClosures = new ArrayList<Closure>(); - mInputs = new ArrayList<UnboundValue>(); - } - - /** - * Adds a closure for a kernel - * - * @param k Kernel ID for the kernel function - * @param returnType Allocation type for the return value - * @param args arguments to the kernel function - * @param globalBindings bindings for global variables - * @return a closure - */ - - public Closure addKernel(Script.KernelID k, Type returnType, Object[] args, - Map<Script.FieldID, Object> globalBindings) { - Closure c = new Closure(mRS, k, returnType, args, globalBindings); - mClosures.add(c); - return c; - } - - /** - * Adds a closure for an invocable function - * - * @param invoke Invoke ID for the invocable function - * @param args arguments to the invocable function - * @param globalBindings bindings for global variables - * @return a closure - */ - - public Closure addInvoke(Script.InvokeID invoke, Object[] args, - Map<Script.FieldID, Object> globalBindings) { - Closure c = new Closure(mRS, invoke, args, globalBindings); - mClosures.add(c); - return c; - } - - /** - * Adds a script group input - * - * @return a unbound value that can be used to create a closure - */ - public UnboundValue addInput() { - UnboundValue unbound = new UnboundValue(); - mInputs.add(unbound); - return unbound; - } - - /** - * Adds a closure for a kernel - * - * @param k Kernel ID for the kernel function - * @param argsAndBindings arguments followed by bindings for global variables - * @return a closure - */ - - public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) { - ArrayList<Object> args = new ArrayList<Object>(); - Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>(); - if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) { - return null; - } - return addKernel(k, returnType, args.toArray(), bindingMap); - } - - /** - * Adds a closure for an invocable function - * - * @param invoke Invoke ID for the invocable function - * @param argsAndBindings arguments followed by bindings for global variables - * @return a closure - */ - - public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) { - ArrayList<Object> args = new ArrayList<Object>(); - Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>(); - if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) { - return null; - } - return addInvoke(invoke, args.toArray(), bindingMap); - } - - /** - * Creates a script group - * - * @param name name for the script group. Legal names can only contain letters, digits, - * '-', or '_'. The name can be no longer than 100 characters. - * @param outputs futures intended as outputs of the script group - * @return a script group - */ - - public ScriptGroup2 create(String name, Future... outputs) { - if (name == null || name.isEmpty() || name.length() > 100 || - !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) { - throw new RSIllegalArgumentException("invalid script group name"); - } - ScriptGroup2 ret = new ScriptGroup2(mRS, name, mClosures, mInputs, outputs); - return ret; - } - - private boolean seperateArgsAndBindings(Object[] argsAndBindings, - ArrayList<Object> args, - Map<Script.FieldID, Object> bindingMap) { - int i; - for (i = 0; i < argsAndBindings.length; i++) { - if (argsAndBindings[i] instanceof Binding) { - break; - } - args.add(argsAndBindings[i]); - } - - for (; i < argsAndBindings.length; i++) { - if (!(argsAndBindings[i] instanceof Binding)) { - return false; - } - Binding b = (Binding)argsAndBindings[i]; - bindingMap.put(b.getField(), b.getValue()); - } - - return true; - } - - } -} |