summaryrefslogtreecommitdiffstats
path: root/rs
diff options
context:
space:
mode:
authorYang Ni <yangni@google.com>2015-04-22 16:00:08 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-04-22 16:00:09 +0000
commitdb720ce9f27f8c27ea2287f8bb999825a9bf09e9 (patch)
tree876ac368f8954cc28f96d1026423b45eb2e5e6d5 /rs
parent58f68b56ea84c8d84937d9ec5210bdf71b2b9acc (diff)
parent18314caccd4e4b7cf59f2a18a38181f8e3d88634 (diff)
downloadframeworks_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.java597
-rw-r--r--rs/java/android/renderscript/ScriptGroup2.java592
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;
- }
-
- }
-}