diff options
author | Yang Ni <yangni@google.com> | 2015-04-22 16:00:08 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-04-22 16:00:09 +0000 |
commit | db720ce9f27f8c27ea2287f8bb999825a9bf09e9 (patch) | |
tree | 876ac368f8954cc28f96d1026423b45eb2e5e6d5 /rs | |
parent | 58f68b56ea84c8d84937d9ec5210bdf71b2b9acc (diff) | |
parent | 18314caccd4e4b7cf59f2a18a38181f8e3d88634 (diff) | |
download | frameworks_base-db720ce9f27f8c27ea2287f8bb999825a9bf09e9.zip frameworks_base-db720ce9f27f8c27ea2287f8bb999825a9bf09e9.tar.gz frameworks_base-db720ce9f27f8c27ea2287f8bb999825a9bf09e9.tar.bz2 |
Merge "Move new script group API into ScriptGroup class"
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; - } - - } -} |