blob: 219f16b91baf79f81686862f52f8fc74c035e68b [file] [log] [blame]
Jason Sams423ebcb2012-08-10 15:40:53 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.renderscript;
18
Yang Ni18314ca2015-04-17 16:51:55 -070019import android.util.Log;
20import android.util.Pair;
Jason Sams08a81582012-09-18 12:32:10 -070021import java.util.ArrayList;
Yang Ni18314ca2015-04-17 16:51:55 -070022import java.util.HashMap;
23import java.util.List;
24import java.util.Map;
Jason Sams423ebcb2012-08-10 15:40:53 -070025
26/**
Yang Ni18314ca2015-04-17 16:51:55 -070027 * A group of kernels that are executed
28 * together with one execution call as if they were a single kernel
Jason Sams08a81582012-09-18 12:32:10 -070029 * <p>
Yang Ni18314ca2015-04-17 16:51:55 -070030 * In addition to kernels, a script group may contain invocable functions as well.
31 * A script group may take inputs and generate outputs, which are consumed and
32 * produced by its member kernels.
33 * Inside a script group, outputs from one kernel can be passed to another kernel as inputs.
34 * The API disallows cyclic dependencies among kernels in a script group,
35 * effectively making it a directed acyclic graph (DAG) of kernels.
Tim Murray2a603892012-10-10 14:21:46 -070036 * <p>
Yang Ni18314ca2015-04-17 16:51:55 -070037 * Grouping kernels together allows for more efficient execution. For example,
38 * runtime and compiler optimization can be applied to reduce computation and
39 * communication overhead, and to make better use of the CPU and the GPU.
Jason Sams423ebcb2012-08-10 15:40:53 -070040 **/
Jason Sams08a81582012-09-18 12:32:10 -070041public final class ScriptGroup extends BaseObj {
Yang Ni18314ca2015-04-17 16:51:55 -070042 private static final String TAG = "ScriptGroup";
Jason Sams423ebcb2012-08-10 15:40:53 -070043 IO mOutputs[];
44 IO mInputs[];
45
46 static class IO {
Jason Sams08a81582012-09-18 12:32:10 -070047 Script.KernelID mKID;
Jason Sams423ebcb2012-08-10 15:40:53 -070048 Allocation mAllocation;
Jason Sams423ebcb2012-08-10 15:40:53 -070049
Jason Sams08a81582012-09-18 12:32:10 -070050 IO(Script.KernelID s) {
51 mKID = s;
Jason Sams423ebcb2012-08-10 15:40:53 -070052 }
53 }
54
Jason Sams08a81582012-09-18 12:32:10 -070055 static class ConnectLine {
56 ConnectLine(Type t, Script.KernelID from, Script.KernelID to) {
57 mFrom = from;
58 mToK = to;
Jason Sams423ebcb2012-08-10 15:40:53 -070059 mAllocationType = t;
60 }
61
Jason Sams08a81582012-09-18 12:32:10 -070062 ConnectLine(Type t, Script.KernelID from, Script.FieldID to) {
63 mFrom = from;
64 mToF = to;
65 mAllocationType = t;
Jason Sams423ebcb2012-08-10 15:40:53 -070066 }
Jason Sams08a81582012-09-18 12:32:10 -070067
68 Script.FieldID mToF;
69 Script.KernelID mToK;
70 Script.KernelID mFrom;
71 Type mAllocationType;
Jason Sams423ebcb2012-08-10 15:40:53 -070072 }
73
74 static class Node {
75 Script mScript;
Jason Sams08a81582012-09-18 12:32:10 -070076 ArrayList<Script.KernelID> mKernels = new ArrayList<Script.KernelID>();
77 ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>();
78 ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>();
Tim Murray2a603892012-10-10 14:21:46 -070079 int dagNumber;
Jason Sams423ebcb2012-08-10 15:40:53 -070080
81 Node mNext;
82
83 Node(Script s) {
84 mScript = s;
85 }
Jason Sams423ebcb2012-08-10 15:40:53 -070086 }
87
88
Yang Ni18314ca2015-04-17 16:51:55 -070089 /**
90 * An opaque class for closures
91 * <p>
92 * A closure represents a function call to a kernel or invocable function,
93 * combined with arguments and values for global variables. A closure is
94 * created using the {@link android.renderscript.ScriptGroup.Builder2#addKernel} or
95 * {@link android.renderscript.ScriptGroup.Builder2#addInvoke}
96 * method.
97 */
98
99 public static final class Closure extends BaseObj {
100 private Object[] mArgs;
101 private Allocation mReturnValue;
102 private Map<Script.FieldID, Object> mBindings;
103
104 private Future mReturnFuture;
105 private Map<Script.FieldID, Future> mGlobalFuture;
106
107 private FieldPacker mFP;
108
109 private static final String TAG = "Closure";
110
111 Closure(long id, RenderScript rs) {
112 super(id, rs);
113 }
114
115 Closure(RenderScript rs, Script.KernelID kernelID, Type returnType,
116 Object[] args, Map<Script.FieldID, Object> globals) {
117 super(0, rs);
118
119 mArgs = args;
120 mReturnValue = Allocation.createTyped(rs, returnType);
121 mBindings = globals;
122 mGlobalFuture = new HashMap<Script.FieldID, Future>();
123
124 int numValues = args.length + globals.size();
125
126 long[] fieldIDs = new long[numValues];
127 long[] values = new long[numValues];
128 int[] sizes = new int[numValues];
129 long[] depClosures = new long[numValues];
130 long[] depFieldIDs = new long[numValues];
131
132 int i;
133 for (i = 0; i < args.length; i++) {
Yang Ni18314ca2015-04-17 16:51:55 -0700134 fieldIDs[i] = 0;
Yang Ni870767e2015-05-18 10:56:47 -0700135 retrieveValueAndDependenceInfo(rs, i, null, args[i],
136 values, sizes, depClosures, depFieldIDs);
Yang Ni18314ca2015-04-17 16:51:55 -0700137 }
Yang Ni18314ca2015-04-17 16:51:55 -0700138 for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
139 Object obj = entry.getValue();
140 Script.FieldID fieldID = entry.getKey();
141 fieldIDs[i] = fieldID.getID(rs);
Yang Ni870767e2015-05-18 10:56:47 -0700142 retrieveValueAndDependenceInfo(rs, i, fieldID, obj,
143 values, sizes, depClosures, depFieldIDs);
Yang Ni18314ca2015-04-17 16:51:55 -0700144 i++;
145 }
146
147 long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs),
148 fieldIDs, values, sizes, depClosures, depFieldIDs);
149
150 setID(id);
Yang Ni6484b6b2016-03-24 09:40:32 -0700151
152 guard.open("destroy");
Yang Ni18314ca2015-04-17 16:51:55 -0700153 }
154
155 Closure(RenderScript rs, Script.InvokeID invokeID,
156 Object[] args, Map<Script.FieldID, Object> globals) {
157 super(0, rs);
158 mFP = FieldPacker.createFromArray(args);
159
160 mArgs = args;
161 mBindings = globals;
162 mGlobalFuture = new HashMap<Script.FieldID, Future>();
163
164 int numValues = globals.size();
165
166 long[] fieldIDs = new long[numValues];
167 long[] values = new long[numValues];
168 int[] sizes = new int[numValues];
169 long[] depClosures = new long[numValues];
170 long[] depFieldIDs = new long[numValues];
171
172 int i = 0;
173 for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
174 Object obj = entry.getValue();
175 Script.FieldID fieldID = entry.getKey();
176 fieldIDs[i] = fieldID.getID(rs);
Yang Ni870767e2015-05-18 10:56:47 -0700177 retrieveValueAndDependenceInfo(rs, i, fieldID, obj, values,
178 sizes, depClosures, depFieldIDs);
Yang Ni18314ca2015-04-17 16:51:55 -0700179 i++;
180 }
181
182 long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs,
183 values, sizes);
184
185 setID(id);
Yang Ni6484b6b2016-03-24 09:40:32 -0700186
187 guard.open("destroy");
Yang Ni18314ca2015-04-17 16:51:55 -0700188 }
189
Yang Ni870767e2015-05-18 10:56:47 -0700190 private void retrieveValueAndDependenceInfo(RenderScript rs,
191 int index, Script.FieldID fid, Object obj,
Yang Ni18314ca2015-04-17 16:51:55 -0700192 long[] values, int[] sizes,
193 long[] depClosures,
194 long[] depFieldIDs) {
195
196 if (obj instanceof Future) {
197 Future f = (Future)obj;
198 obj = f.getValue();
199 depClosures[index] = f.getClosure().getID(rs);
200 Script.FieldID fieldID = f.getFieldID();
201 depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0;
Yang Ni18314ca2015-04-17 16:51:55 -0700202 } else {
203 depClosures[index] = 0;
204 depFieldIDs[index] = 0;
205 }
206
Yang Ni870767e2015-05-18 10:56:47 -0700207 if (obj instanceof Input) {
208 Input unbound = (Input)obj;
209 if (index < mArgs.length) {
210 unbound.addReference(this, index);
211 } else {
212 unbound.addReference(this, fid);
213 }
214 values[index] = 0;
215 sizes[index] = 0;
216 } else {
217 ValueAndSize vs = new ValueAndSize(rs, obj);
218 values[index] = vs.value;
219 sizes[index] = vs.size;
220 }
Yang Ni18314ca2015-04-17 16:51:55 -0700221 }
222
223 /**
224 * Returns the future for the return value
225 *
226 * @return a future
227 */
228
229 public Future getReturn() {
230 if (mReturnFuture == null) {
231 mReturnFuture = new Future(this, null, mReturnValue);
232 }
233
234 return mReturnFuture;
235 }
236
237 /**
238 * Returns the future for a global variable
239 *
240 * @param field the field ID for the global variable
241 * @return a future
242 */
243
244 public Future getGlobal(Script.FieldID field) {
245 Future f = mGlobalFuture.get(field);
246
247 if (f == null) {
248 // If the field is not bound to this closure, this will return a future
249 // without an associated value (reference). So this is not working for
250 // cross-module (cross-script) linking in this case where a field not
251 // explicitly bound.
Yang Ni870767e2015-05-18 10:56:47 -0700252 Object obj = mBindings.get(field);
253 if (obj instanceof Future) {
254 obj = ((Future)obj).getValue();
255 }
256 f = new Future(this, field, obj);
Yang Ni18314ca2015-04-17 16:51:55 -0700257 mGlobalFuture.put(field, f);
258 }
259
260 return f;
261 }
262
263 void setArg(int index, Object obj) {
Yang Ni870767e2015-05-18 10:56:47 -0700264 if (obj instanceof Future) {
265 obj = ((Future)obj).getValue();
266 }
Yang Ni18314ca2015-04-17 16:51:55 -0700267 mArgs[index] = obj;
268 ValueAndSize vs = new ValueAndSize(mRS, obj);
269 mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size);
270 }
271
272 void setGlobal(Script.FieldID fieldID, Object obj) {
Yang Ni870767e2015-05-18 10:56:47 -0700273 if (obj instanceof Future) {
274 obj = ((Future)obj).getValue();
275 }
Yang Ni18314ca2015-04-17 16:51:55 -0700276 mBindings.put(fieldID, obj);
277 ValueAndSize vs = new ValueAndSize(mRS, obj);
278 mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size);
279 }
280
281 private static final class ValueAndSize {
282 public ValueAndSize(RenderScript rs, Object obj) {
283 if (obj instanceof Allocation) {
284 value = ((Allocation)obj).getID(rs);
Yang Ni263cc902015-11-10 13:27:04 -0800285 // Special value for size to tell the runtime and driver that
286 // the value is an Allocation
Yang Ni18314ca2015-04-17 16:51:55 -0700287 size = -1;
288 } else if (obj instanceof Boolean) {
289 value = ((Boolean)obj).booleanValue() ? 1 : 0;
290 size = 4;
291 } else if (obj instanceof Integer) {
292 value = ((Integer)obj).longValue();
293 size = 4;
294 } else if (obj instanceof Long) {
295 value = ((Long)obj).longValue();
296 size = 8;
297 } else if (obj instanceof Float) {
Yang Ni263cc902015-11-10 13:27:04 -0800298 value = Float.floatToRawIntBits(((Float)obj).floatValue());
Yang Ni18314ca2015-04-17 16:51:55 -0700299 size = 4;
300 } else if (obj instanceof Double) {
Yang Ni263cc902015-11-10 13:27:04 -0800301 value = Double.doubleToRawLongBits(((Double)obj).doubleValue());
Yang Ni18314ca2015-04-17 16:51:55 -0700302 size = 8;
303 }
304 }
305 public long value;
306 public int size;
307 }
308 }
309
310 /**
311 * An opaque class for futures
312 * <p>
313 * A future represents an output of a closure, either the return value of
314 * the function, or the value of a global variable written by the function.
315 * A future is created by calling the {@link Closure#getReturn} or
316 * {@link Closure#getGlobal} method.
317 */
318
319 public static final class Future {
320 Closure mClosure;
321 Script.FieldID mFieldID;
322 Object mValue;
323
324 Future(Closure closure, Script.FieldID fieldID, Object value) {
325 mClosure = closure;
326 mFieldID = fieldID;
327 mValue = value;
328 }
329
330 Closure getClosure() { return mClosure; }
331 Script.FieldID getFieldID() { return mFieldID; }
332 Object getValue() { return mValue; }
333 }
334
335 /**
336 * An opaque class for script group inputs
337 * <p>
338 * Created by calling the {@link Builder2#addInput} method. The value
339 * is assigned in {@link ScriptGroup#execute(Object...)} method as
340 * one of its arguments. Arguments to the execute method should be in
341 * the same order as intputs are added using the addInput method.
342 */
343
344 public static final class Input {
345 // Either mFieldID or mArgIndex should be set but not both.
346 List<Pair<Closure, Script.FieldID>> mFieldID;
347 // -1 means unset. Legal values are 0 .. n-1, where n is the number of
348 // arguments for the referencing closure.
349 List<Pair<Closure, Integer>> mArgIndex;
Yang Ni870767e2015-05-18 10:56:47 -0700350 Object mValue;
Yang Ni18314ca2015-04-17 16:51:55 -0700351
352 Input() {
353 mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>();
354 mArgIndex = new ArrayList<Pair<Closure, Integer>>();
355 }
356
357 void addReference(Closure closure, int index) {
358 mArgIndex.add(Pair.create(closure, Integer.valueOf(index)));
359 }
360
361 void addReference(Closure closure, Script.FieldID fieldID) {
362 mFieldID.add(Pair.create(closure, fieldID));
363 }
364
365 void set(Object value) {
Yang Ni870767e2015-05-18 10:56:47 -0700366 mValue = value;
Yang Ni18314ca2015-04-17 16:51:55 -0700367 for (Pair<Closure, Integer> p : mArgIndex) {
368 Closure closure = p.first;
369 int index = p.second.intValue();
370 closure.setArg(index, value);
371 }
372 for (Pair<Closure, Script.FieldID> p : mFieldID) {
373 Closure closure = p.first;
374 Script.FieldID fieldID = p.second;
375 closure.setGlobal(fieldID, value);
376 }
377 }
Yang Ni870767e2015-05-18 10:56:47 -0700378
379 Object get() { return mValue; }
Yang Ni18314ca2015-04-17 16:51:55 -0700380 }
381
382 private String mName;
383 private List<Closure> mClosures;
384 private List<Input> mInputs2;
385 private Future[] mOutputs2;
386
Tim Murray460a0492013-11-19 12:45:54 -0800387 ScriptGroup(long id, RenderScript rs) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700388 super(id, rs);
Yang Ni6484b6b2016-03-24 09:40:32 -0700389 guard.open("destroy");
Jason Sams423ebcb2012-08-10 15:40:53 -0700390 }
391
Yang Ni18314ca2015-04-17 16:51:55 -0700392 ScriptGroup(RenderScript rs, String name, List<Closure> closures,
393 List<Input> inputs, Future[] outputs) {
394 super(0, rs);
395 mName = name;
396 mClosures = closures;
397 mInputs2 = inputs;
398 mOutputs2 = outputs;
399
400 long[] closureIDs = new long[closures.size()];
401 for (int i = 0; i < closureIDs.length; i++) {
402 closureIDs[i] = closures.get(i).getID(rs);
403 }
Yang Ni15fcf612016-03-10 16:12:31 -0800404 long id = rs.nScriptGroup2Create(name, RenderScript.getCachePath(), closureIDs);
Yang Ni18314ca2015-04-17 16:51:55 -0700405 setID(id);
Yang Ni6484b6b2016-03-24 09:40:32 -0700406 guard.open("destroy");
Yang Ni18314ca2015-04-17 16:51:55 -0700407 }
408
409 /**
410 * Executes a script group
411 *
Yang Ni43563892015-05-12 13:53:38 -0700412 * @param inputs Values for inputs to the script group, in the order as the
413 * inputs are added via {@link Builder2#addInput}.
414 * @return Outputs of the script group as an array of objects, in the order
415 * as futures are passed to {@link Builder2#create}.
Yang Ni18314ca2015-04-17 16:51:55 -0700416 */
417
418 public Object[] execute(Object... inputs) {
419 if (inputs.length < mInputs2.size()) {
420 Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
421 "less than expected " + mInputs2.size());
422 return null;
423 }
424
425 if (inputs.length > mInputs2.size()) {
426 Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
427 "more than expected " + mInputs2.size());
428 }
429
430 for (int i = 0; i < mInputs2.size(); i++) {
431 Object obj = inputs[i];
432 if (obj instanceof Future || obj instanceof Input) {
433 Log.e(TAG, this.toString() + ": input " + i +
434 " is a future or unbound value");
435 return null;
436 }
437 Input unbound = mInputs2.get(i);
438 unbound.set(obj);
439 }
440
441 mRS.nScriptGroup2Execute(getID(mRS));
442
443 Object[] outputObjs = new Object[mOutputs2.length];
444 int i = 0;
445 for (Future f : mOutputs2) {
Yang Ni870767e2015-05-18 10:56:47 -0700446 Object output = f.getValue();
447 if (output instanceof Input) {
448 output = ((Input)output).get();
449 }
450 outputObjs[i++] = output;
Yang Ni18314ca2015-04-17 16:51:55 -0700451 }
452 return outputObjs;
453 }
454
Jason Sams08a81582012-09-18 12:32:10 -0700455 /**
456 * Sets an input of the ScriptGroup. This specifies an
Tim Murrayc11e25c2013-04-09 11:01:01 -0700457 * Allocation to be used for kernels that require an input
458 * Allocation provided from outside of the ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -0700459 *
Yang Ni18314ca2015-04-17 16:51:55 -0700460 * @deprecated Set arguments to {@link #execute(Object...)} instead.
461 *
Jason Sams08a81582012-09-18 12:32:10 -0700462 * @param s The ID of the kernel where the allocation should be
463 * connected.
464 * @param a The allocation to connect.
465 */
466 public void setInput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700467 for (int ct=0; ct < mInputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700468 if (mInputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700469 mInputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700470 mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a));
Jason Sams423ebcb2012-08-10 15:40:53 -0700471 return;
472 }
473 }
474 throw new RSIllegalArgumentException("Script not found");
475 }
476
Jason Sams08a81582012-09-18 12:32:10 -0700477 /**
478 * Sets an output of the ScriptGroup. This specifies an
Tim Murrayc11e25c2013-04-09 11:01:01 -0700479 * Allocation to be used for the kernels that require an output
480 * Allocation visible after the ScriptGroup is executed.
Jason Sams08a81582012-09-18 12:32:10 -0700481 *
Yang Ni18314ca2015-04-17 16:51:55 -0700482 * @deprecated Use return value of {@link #execute(Object...)} instead.
483 *
Jason Sams08a81582012-09-18 12:32:10 -0700484 * @param s The ID of the kernel where the allocation should be
485 * connected.
486 * @param a The allocation to connect.
487 */
488 public void setOutput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700489 for (int ct=0; ct < mOutputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700490 if (mOutputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700491 mOutputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700492 mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
Jason Sams423ebcb2012-08-10 15:40:53 -0700493 return;
494 }
495 }
496 throw new RSIllegalArgumentException("Script not found");
497 }
498
Jason Sams08a81582012-09-18 12:32:10 -0700499 /**
500 * Execute the ScriptGroup. This will run all the kernels in
Tim Murrayc11e25c2013-04-09 11:01:01 -0700501 * the ScriptGroup. No internal connection results will be visible
502 * after execution of the ScriptGroup.
Yang Ni18314ca2015-04-17 16:51:55 -0700503 *
504 * @deprecated Use {@link #execute} instead.
505 *
Jason Sams08a81582012-09-18 12:32:10 -0700506 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700507 public void execute() {
Jason Sams08a81582012-09-18 12:32:10 -0700508 mRS.nScriptGroupExecute(getID(mRS));
Jason Sams423ebcb2012-08-10 15:40:53 -0700509 }
510
511
Jason Sams08a81582012-09-18 12:32:10 -0700512 /**
Tim Murrayc11e25c2013-04-09 11:01:01 -0700513 * Helper class to build a ScriptGroup. A ScriptGroup is
514 * created in two steps.
Jason Sams08a81582012-09-18 12:32:10 -0700515 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700516 * First, all kernels to be used by the ScriptGroup should be added.
Jason Sams08a81582012-09-18 12:32:10 -0700517 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700518 * Second, add connections between kernels. There are two types
519 * of connections: kernel to kernel and kernel to field.
520 * Kernel to kernel allows a kernel's output to be passed to
521 * another kernel as input. Kernel to field allows the output of
522 * one kernel to be bound as a script global. Kernel to kernel is
523 * higher performance and should be used where possible.
Jason Sams08a81582012-09-18 12:32:10 -0700524 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700525 * A ScriptGroup must contain a single directed acyclic graph (DAG); it
526 * cannot contain cycles. Currently, all kernels used in a ScriptGroup
527 * must come from different Script objects. Additionally, all kernels
528 * in a ScriptGroup must have at least one input, output, or internal
529 * connection.
530 * <p>
531 * Once all connections are made, a call to {@link #create} will
Jason Sams08a81582012-09-18 12:32:10 -0700532 * return the ScriptGroup object.
533 *
Yang Ni18314ca2015-04-17 16:51:55 -0700534 * @deprecated Use {@link Builder2} instead.
535 *
Jason Sams08a81582012-09-18 12:32:10 -0700536 */
537 public static final class Builder {
538 private RenderScript mRS;
539 private ArrayList<Node> mNodes = new ArrayList<Node>();
540 private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>();
541 private int mKernelCount;
Jason Sams423ebcb2012-08-10 15:40:53 -0700542
Jason Sams08a81582012-09-18 12:32:10 -0700543 /**
Tim Murrayc11e25c2013-04-09 11:01:01 -0700544 * Create a Builder for generating a ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -0700545 *
546 *
Tim Murrayc11e25c2013-04-09 11:01:01 -0700547 * @param rs The RenderScript context.
Jason Sams08a81582012-09-18 12:32:10 -0700548 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700549 public Builder(RenderScript rs) {
550 mRS = rs;
551 }
552
Tim Murray091f7cc2012-10-12 12:02:18 -0700553 // do a DFS from original node, looking for original node
554 // any cycle that could be created must contain original node
555 private void validateCycle(Node target, Node original) {
556 for (int ct = 0; ct < target.mOutputs.size(); ct++) {
557 final ConnectLine cl = target.mOutputs.get(ct);
Jason Sams08a81582012-09-18 12:32:10 -0700558 if (cl.mToK != null) {
559 Node tn = findNode(cl.mToK.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700560 if (tn.equals(original)) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700561 throw new RSInvalidStateException("Loops in group not allowed.");
562 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700563 validateCycle(tn, original);
Jason Sams08a81582012-09-18 12:32:10 -0700564 }
565 if (cl.mToF != null) {
566 Node tn = findNode(cl.mToF.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700567 if (tn.equals(original)) {
Jason Sams08a81582012-09-18 12:32:10 -0700568 throw new RSInvalidStateException("Loops in group not allowed.");
569 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700570 validateCycle(tn, original);
Tim Murray2a603892012-10-10 14:21:46 -0700571 }
572 }
573 }
574
575 private void mergeDAGs(int valueUsed, int valueKilled) {
576 for (int ct=0; ct < mNodes.size(); ct++) {
577 if (mNodes.get(ct).dagNumber == valueKilled)
578 mNodes.get(ct).dagNumber = valueUsed;
579 }
580 }
581
582 private void validateDAGRecurse(Node n, int dagNumber) {
583 // combine DAGs if this node has been seen already
584 if (n.dagNumber != 0 && n.dagNumber != dagNumber) {
585 mergeDAGs(n.dagNumber, dagNumber);
586 return;
587 }
588
589 n.dagNumber = dagNumber;
590 for (int ct=0; ct < n.mOutputs.size(); ct++) {
591 final ConnectLine cl = n.mOutputs.get(ct);
592 if (cl.mToK != null) {
593 Node tn = findNode(cl.mToK.mScript);
594 validateDAGRecurse(tn, dagNumber);
595 }
596 if (cl.mToF != null) {
597 Node tn = findNode(cl.mToF.mScript);
598 validateDAGRecurse(tn, dagNumber);
599 }
600 }
601 }
602
603 private void validateDAG() {
604 for (int ct=0; ct < mNodes.size(); ct++) {
605 Node n = mNodes.get(ct);
606 if (n.mInputs.size() == 0) {
607 if (n.mOutputs.size() == 0 && mNodes.size() > 1) {
Yang Ni870767e2015-05-18 10:56:47 -0700608 String msg = "Groups cannot contain unconnected scripts";
609 throw new RSInvalidStateException(msg);
Tim Murray2a603892012-10-10 14:21:46 -0700610 }
611 validateDAGRecurse(n, ct+1);
612 }
613 }
614 int dagNumber = mNodes.get(0).dagNumber;
615 for (int ct=0; ct < mNodes.size(); ct++) {
616 if (mNodes.get(ct).dagNumber != dagNumber) {
617 throw new RSInvalidStateException("Multiple DAGs in group not allowed.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700618 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700619 }
620 }
621
Jason Sams08a81582012-09-18 12:32:10 -0700622 private Node findNode(Script s) {
623 for (int ct=0; ct < mNodes.size(); ct++) {
624 if (s == mNodes.get(ct).mScript) {
625 return mNodes.get(ct);
Jason Sams423ebcb2012-08-10 15:40:53 -0700626 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700627 }
628 return null;
629 }
630
Jason Sams08a81582012-09-18 12:32:10 -0700631 private Node findNode(Script.KernelID k) {
632 for (int ct=0; ct < mNodes.size(); ct++) {
633 Node n = mNodes.get(ct);
634 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
635 if (k == n.mKernels.get(ct2)) {
636 return n;
Jason Sams423ebcb2012-08-10 15:40:53 -0700637 }
638 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700639 }
Jason Sams08a81582012-09-18 12:32:10 -0700640 return null;
641 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700642
Jason Sams08a81582012-09-18 12:32:10 -0700643 /**
644 * Adds a Kernel to the group.
645 *
646 *
647 * @param k The kernel to add.
648 *
649 * @return Builder Returns this.
650 */
651 public Builder addKernel(Script.KernelID k) {
652 if (mLines.size() != 0) {
653 throw new RSInvalidStateException(
654 "Kernels may not be added once connections exist.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700655 }
Jason Sams08a81582012-09-18 12:32:10 -0700656
657 //android.util.Log.v("RSR", "addKernel 1 k=" + k);
658 if (findNode(k) != null) {
659 return this;
660 }
661 //android.util.Log.v("RSR", "addKernel 2 ");
662 mKernelCount++;
663 Node n = findNode(k.mScript);
664 if (n == null) {
665 //android.util.Log.v("RSR", "addKernel 3 ");
666 n = new Node(k.mScript);
667 mNodes.add(n);
668 }
669 n.mKernels.add(k);
670 return this;
671 }
672
673 /**
674 * Adds a connection to the group.
675 *
676 *
677 * @param t The type of the connection. This is used to
678 * determine the kernel launch sizes on the source side
679 * of this connection.
680 * @param from The source for the connection.
681 * @param to The destination of the connection.
682 *
683 * @return Builder Returns this
684 */
685 public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
686 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
687
688 Node nf = findNode(from);
689 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700690 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700691 }
692
693 Node nt = findNode(to.mScript);
694 if (nt == null) {
695 throw new RSInvalidStateException("To script not found.");
696 }
697
698 ConnectLine cl = new ConnectLine(t, from, to);
699 mLines.add(new ConnectLine(t, from, to));
700
701 nf.mOutputs.add(cl);
702 nt.mInputs.add(cl);
Jason Sams423ebcb2012-08-10 15:40:53 -0700703
Tim Murray091f7cc2012-10-12 12:02:18 -0700704 validateCycle(nf, nf);
Jason Sams423ebcb2012-08-10 15:40:53 -0700705 return this;
706 }
707
Jason Sams08a81582012-09-18 12:32:10 -0700708 /**
709 * Adds a connection to the group.
710 *
711 *
712 * @param t The type of the connection. This is used to
713 * determine the kernel launch sizes for both sides of
714 * this connection.
715 * @param from The source for the connection.
716 * @param to The destination of the connection.
717 *
718 * @return Builder Returns this
719 */
720 public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
721 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
722
723 Node nf = findNode(from);
724 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700725 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700726 }
727
728 Node nt = findNode(to);
729 if (nt == null) {
730 throw new RSInvalidStateException("To script not found.");
731 }
732
733 ConnectLine cl = new ConnectLine(t, from, to);
734 mLines.add(new ConnectLine(t, from, to));
735
736 nf.mOutputs.add(cl);
737 nt.mInputs.add(cl);
738
Tim Murray091f7cc2012-10-12 12:02:18 -0700739 validateCycle(nf, nf);
Jason Sams08a81582012-09-18 12:32:10 -0700740 return this;
741 }
742
743
744
745 /**
746 * Creates the Script group.
747 *
748 *
749 * @return ScriptGroup The new ScriptGroup
750 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700751 public ScriptGroup create() {
Tim Murray2a603892012-10-10 14:21:46 -0700752
753 if (mNodes.size() == 0) {
754 throw new RSInvalidStateException("Empty script groups are not allowed");
755 }
756
757 // reset DAG numbers in case we're building a second group
758 for (int ct=0; ct < mNodes.size(); ct++) {
759 mNodes.get(ct).dagNumber = 0;
760 }
761 validateDAG();
762
Jason Sams08a81582012-09-18 12:32:10 -0700763 ArrayList<IO> inputs = new ArrayList<IO>();
764 ArrayList<IO> outputs = new ArrayList<IO>();
Jason Sams423ebcb2012-08-10 15:40:53 -0700765
Ashok Bhat98071552014-02-12 09:54:43 +0000766 long[] kernels = new long[mKernelCount];
Jason Sams08a81582012-09-18 12:32:10 -0700767 int idx = 0;
768 for (int ct=0; ct < mNodes.size(); ct++) {
769 Node n = mNodes.get(ct);
770 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
771 final Script.KernelID kid = n.mKernels.get(ct2);
Ashok Bhat98071552014-02-12 09:54:43 +0000772 kernels[idx++] = kid.getID(mRS);
Jason Sams423ebcb2012-08-10 15:40:53 -0700773
Jason Sams08a81582012-09-18 12:32:10 -0700774 boolean hasInput = false;
775 boolean hasOutput = false;
776 for (int ct3=0; ct3 < n.mInputs.size(); ct3++) {
777 if (n.mInputs.get(ct3).mToK == kid) {
778 hasInput = true;
779 }
780 }
781 for (int ct3=0; ct3 < n.mOutputs.size(); ct3++) {
782 if (n.mOutputs.get(ct3).mFrom == kid) {
783 hasOutput = true;
784 }
785 }
786 if (!hasInput) {
787 inputs.add(new IO(kid));
788 }
789 if (!hasOutput) {
790 outputs.add(new IO(kid));
791 }
792
793 }
794 }
795 if (idx != mKernelCount) {
796 throw new RSRuntimeException("Count mismatch, should not happen.");
797 }
798
Ashok Bhat98071552014-02-12 09:54:43 +0000799 long[] src = new long[mLines.size()];
800 long[] dstk = new long[mLines.size()];
801 long[] dstf = new long[mLines.size()];
802 long[] types = new long[mLines.size()];
Jason Sams08a81582012-09-18 12:32:10 -0700803
804 for (int ct=0; ct < mLines.size(); ct++) {
805 ConnectLine cl = mLines.get(ct);
Ashok Bhat98071552014-02-12 09:54:43 +0000806 src[ct] = cl.mFrom.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700807 if (cl.mToK != null) {
Ashok Bhat98071552014-02-12 09:54:43 +0000808 dstk[ct] = cl.mToK.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700809 }
810 if (cl.mToF != null) {
Ashok Bhat98071552014-02-12 09:54:43 +0000811 dstf[ct] = cl.mToF.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700812 }
Ashok Bhat98071552014-02-12 09:54:43 +0000813 types[ct] = cl.mAllocationType.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700814 }
815
Tim Murray460a0492013-11-19 12:45:54 -0800816 long id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
Jason Sams08a81582012-09-18 12:32:10 -0700817 if (id == 0) {
818 throw new RSRuntimeException("Object creation error, should not happen.");
819 }
820
821 ScriptGroup sg = new ScriptGroup(id, mRS);
822 sg.mOutputs = new IO[outputs.size()];
823 for (int ct=0; ct < outputs.size(); ct++) {
824 sg.mOutputs[ct] = outputs.get(ct);
825 }
826
827 sg.mInputs = new IO[inputs.size()];
828 for (int ct=0; ct < inputs.size(); ct++) {
829 sg.mInputs[ct] = inputs.get(ct);
830 }
831
Jason Sams423ebcb2012-08-10 15:40:53 -0700832 return sg;
833 }
834
835 }
836
Yang Ni18314ca2015-04-17 16:51:55 -0700837 /**
838 * Represents a binding of a value to a global variable in a
839 * kernel or invocable function. Used in closure creation.
840 */
841
842 public static final class Binding {
843 private final Script.FieldID mField;
844 private final Object mValue;
845
846 /**
847 * Returns a Binding object that binds value to field
848 *
849 * @param field the Script.FieldID of the global variable
850 * @param value the value
851 */
852
853 public Binding(Script.FieldID field, Object value) {
854 mField = field;
855 mValue = value;
856 }
857
858 /**
859 * Returns the field ID
860 */
861
Yang Ni77eba482015-05-15 09:56:49 -0700862 Script.FieldID getField() { return mField; }
Yang Ni18314ca2015-04-17 16:51:55 -0700863
864 /**
865 * Returns the value
866 */
867
Yang Ni77eba482015-05-15 09:56:49 -0700868 Object getValue() { return mValue; }
Yang Ni18314ca2015-04-17 16:51:55 -0700869 }
870
871 /**
872 * The builder class for creating script groups
873 * <p>
874 * A script group is created using closures (see class {@link Closure}).
875 * A closure is a function call to a kernel or
876 * invocable function. Each function argument or global variable accessed inside
877 * the function is bound to 1) a known value, 2) a script group input
878 * (see class {@link Input}), or 3) a
879 * future (see class {@link Future}).
880 * A future is the output of a closure, either the return value of the
881 * function or a global variable written by that function.
882 * <p>
883 * Closures are created using the {@link #addKernel} or {@link #addInvoke}
884 * methods.
885 * When a closure is created, futures from previously created closures
886 * can be used as its inputs.
887 * External script group inputs can be used as inputs to individual closures as well.
888 * An external script group input is created using the {@link #addInput} method.
889 * A script group is created by a call to the {@link #create} method, which
890 * accepts an array of futures as the outputs for the script group.
891 * <p>
892 * Closures in a script group can be evaluated in any order as long as the
893 * following conditions are met:
894 * 1) a closure must be evaluated before any other closures that take its
895 * futures as inputs;
896 * 2) all closures added before an invoke closure must be evaluated
897 * before it;
898 * and 3) all closures added after an invoke closure must be evaluated after
899 * it.
900 * As a special case, the order that the closures are added is a legal
901 * evaluation order. However, other evaluation orders are possible, including
902 * concurrently evaluating independent closures.
903 */
904
905 public static final class Builder2 {
906 RenderScript mRS;
907 List<Closure> mClosures;
908 List<Input> mInputs;
909 private static final String TAG = "ScriptGroup.Builder2";
910
911 /**
912 * Returns a Builder object
913 *
914 * @param rs the RenderScript context
915 */
916 public Builder2(RenderScript rs) {
917 mRS = rs;
918 mClosures = new ArrayList<Closure>();
919 mInputs = new ArrayList<Input>();
920 }
921
922 /**
923 * Adds a closure for a kernel
924 *
925 * @param k Kernel ID for the kernel function
926 * @param returnType Allocation type for the return value
927 * @param args arguments to the kernel function
928 * @param globalBindings bindings for global variables
929 * @return a closure
930 */
931
932 private Closure addKernelInternal(Script.KernelID k, Type returnType, Object[] args,
933 Map<Script.FieldID, Object> globalBindings) {
934 Closure c = new Closure(mRS, k, returnType, args, globalBindings);
935 mClosures.add(c);
936 return c;
937 }
938
939 /**
940 * Adds a closure for an invocable function
941 *
942 * @param invoke Invoke ID for the invocable function
943 * @param args arguments to the invocable function
944 * @param globalBindings bindings for global variables
945 * @return a closure
946 */
947
948 private Closure addInvokeInternal(Script.InvokeID invoke, Object[] args,
949 Map<Script.FieldID, Object> globalBindings) {
950 Closure c = new Closure(mRS, invoke, args, globalBindings);
951 mClosures.add(c);
952 return c;
953 }
954
955 /**
956 * Adds a script group input
957 *
958 * @return a script group input, which can be used as an argument or a value to
959 * a global variable for creating closures
960 */
961 public Input addInput() {
962 Input unbound = new Input();
963 mInputs.add(unbound);
964 return unbound;
965 }
966
967 /**
968 * Adds a closure for a kernel
969 *
970 * @param k Kernel ID for the kernel function
971 * @param argsAndBindings arguments followed by bindings for global variables
972 * @return a closure
973 */
974
975 public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) {
976 ArrayList<Object> args = new ArrayList<Object>();
977 Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
978 if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
979 return null;
980 }
981 return addKernelInternal(k, returnType, args.toArray(), bindingMap);
982 }
983
984 /**
985 * Adds a closure for an invocable function
986 *
987 * @param invoke Invoke ID for the invocable function
988 * @param argsAndBindings arguments followed by bindings for global variables
989 * @return a closure
990 */
991
992 public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) {
993 ArrayList<Object> args = new ArrayList<Object>();
994 Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
995 if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
996 return null;
997 }
998 return addInvokeInternal(invoke, args.toArray(), bindingMap);
999 }
1000
1001 /**
1002 * Creates a script group
1003 *
1004 * @param name name for the script group. Legal names can only contain letters, digits,
1005 * '-', or '_'. The name can be no longer than 100 characters.
Yang Nidbb6fd52015-06-24 17:06:10 -07001006 * Try to use unique names, to avoid name conflicts and reduce
1007 * the cost of group creation.
Yang Ni18314ca2015-04-17 16:51:55 -07001008 * @param outputs futures intended as outputs of the script group
1009 * @return a script group
1010 */
1011
1012 public ScriptGroup create(String name, Future... outputs) {
1013 if (name == null || name.isEmpty() || name.length() > 100 ||
1014 !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) {
1015 throw new RSIllegalArgumentException("invalid script group name");
1016 }
1017 ScriptGroup ret = new ScriptGroup(mRS, name, mClosures, mInputs, outputs);
1018 return ret;
1019 }
1020
1021 private boolean seperateArgsAndBindings(Object[] argsAndBindings,
1022 ArrayList<Object> args,
1023 Map<Script.FieldID, Object> bindingMap) {
1024 int i;
1025 for (i = 0; i < argsAndBindings.length; i++) {
1026 if (argsAndBindings[i] instanceof Binding) {
1027 break;
1028 }
1029 args.add(argsAndBindings[i]);
1030 }
1031
1032 for (; i < argsAndBindings.length; i++) {
1033 if (!(argsAndBindings[i] instanceof Binding)) {
1034 return false;
1035 }
1036 Binding b = (Binding)argsAndBindings[i];
1037 bindingMap.put(b.getField(), b.getValue());
1038 }
1039
1040 return true;
1041 }
1042
1043 }
Jason Sams423ebcb2012-08-10 15:40:53 -07001044
1045}