blob: 5cdb9cf3b8be6d4cc3b63ba3cf54979c181e4ae0 [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.
Xusong Wang1f8dc652021-01-05 10:09:52 -080040 *
41 * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
42 * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
43 * guide</a> for the proposed alternatives.
Jason Sams423ebcb2012-08-10 15:40:53 -070044 **/
Xusong Wang1f8dc652021-01-05 10:09:52 -080045@Deprecated
Jason Sams08a81582012-09-18 12:32:10 -070046public final class ScriptGroup extends BaseObj {
Yang Ni18314ca2015-04-17 16:51:55 -070047 private static final String TAG = "ScriptGroup";
Jason Sams423ebcb2012-08-10 15:40:53 -070048 IO mOutputs[];
49 IO mInputs[];
50
51 static class IO {
Jason Sams08a81582012-09-18 12:32:10 -070052 Script.KernelID mKID;
Jason Sams423ebcb2012-08-10 15:40:53 -070053 Allocation mAllocation;
Jason Sams423ebcb2012-08-10 15:40:53 -070054
Jason Sams08a81582012-09-18 12:32:10 -070055 IO(Script.KernelID s) {
56 mKID = s;
Jason Sams423ebcb2012-08-10 15:40:53 -070057 }
58 }
59
Jason Sams08a81582012-09-18 12:32:10 -070060 static class ConnectLine {
61 ConnectLine(Type t, Script.KernelID from, Script.KernelID to) {
62 mFrom = from;
63 mToK = to;
Jason Sams423ebcb2012-08-10 15:40:53 -070064 mAllocationType = t;
65 }
66
Jason Sams08a81582012-09-18 12:32:10 -070067 ConnectLine(Type t, Script.KernelID from, Script.FieldID to) {
68 mFrom = from;
69 mToF = to;
70 mAllocationType = t;
Jason Sams423ebcb2012-08-10 15:40:53 -070071 }
Jason Sams08a81582012-09-18 12:32:10 -070072
73 Script.FieldID mToF;
74 Script.KernelID mToK;
75 Script.KernelID mFrom;
76 Type mAllocationType;
Jason Sams423ebcb2012-08-10 15:40:53 -070077 }
78
79 static class Node {
80 Script mScript;
Jason Sams08a81582012-09-18 12:32:10 -070081 ArrayList<Script.KernelID> mKernels = new ArrayList<Script.KernelID>();
82 ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>();
83 ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>();
Tim Murray2a603892012-10-10 14:21:46 -070084 int dagNumber;
Jason Sams423ebcb2012-08-10 15:40:53 -070085
86 Node mNext;
87
88 Node(Script s) {
89 mScript = s;
90 }
Jason Sams423ebcb2012-08-10 15:40:53 -070091 }
92
93
Yang Ni18314ca2015-04-17 16:51:55 -070094 /**
95 * An opaque class for closures
96 * <p>
97 * A closure represents a function call to a kernel or invocable function,
98 * combined with arguments and values for global variables. A closure is
99 * created using the {@link android.renderscript.ScriptGroup.Builder2#addKernel} or
100 * {@link android.renderscript.ScriptGroup.Builder2#addInvoke}
101 * method.
102 */
103
104 public static final class Closure extends BaseObj {
105 private Object[] mArgs;
106 private Allocation mReturnValue;
107 private Map<Script.FieldID, Object> mBindings;
108
109 private Future mReturnFuture;
110 private Map<Script.FieldID, Future> mGlobalFuture;
111
112 private FieldPacker mFP;
113
114 private static final String TAG = "Closure";
115
116 Closure(long id, RenderScript rs) {
117 super(id, rs);
118 }
119
120 Closure(RenderScript rs, Script.KernelID kernelID, Type returnType,
121 Object[] args, Map<Script.FieldID, Object> globals) {
122 super(0, rs);
123
124 mArgs = args;
125 mReturnValue = Allocation.createTyped(rs, returnType);
126 mBindings = globals;
127 mGlobalFuture = new HashMap<Script.FieldID, Future>();
128
129 int numValues = args.length + globals.size();
130
131 long[] fieldIDs = new long[numValues];
132 long[] values = new long[numValues];
133 int[] sizes = new int[numValues];
134 long[] depClosures = new long[numValues];
135 long[] depFieldIDs = new long[numValues];
136
137 int i;
138 for (i = 0; i < args.length; i++) {
Yang Ni18314ca2015-04-17 16:51:55 -0700139 fieldIDs[i] = 0;
Yang Ni870767e2015-05-18 10:56:47 -0700140 retrieveValueAndDependenceInfo(rs, i, null, args[i],
141 values, sizes, depClosures, depFieldIDs);
Yang Ni18314ca2015-04-17 16:51:55 -0700142 }
Yang Ni18314ca2015-04-17 16:51:55 -0700143 for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
144 Object obj = entry.getValue();
145 Script.FieldID fieldID = entry.getKey();
146 fieldIDs[i] = fieldID.getID(rs);
Yang Ni870767e2015-05-18 10:56:47 -0700147 retrieveValueAndDependenceInfo(rs, i, fieldID, obj,
148 values, sizes, depClosures, depFieldIDs);
Yang Ni18314ca2015-04-17 16:51:55 -0700149 i++;
150 }
151
152 long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs),
153 fieldIDs, values, sizes, depClosures, depFieldIDs);
154
155 setID(id);
Yang Ni6484b6b2016-03-24 09:40:32 -0700156
157 guard.open("destroy");
Yang Ni18314ca2015-04-17 16:51:55 -0700158 }
159
160 Closure(RenderScript rs, Script.InvokeID invokeID,
161 Object[] args, Map<Script.FieldID, Object> globals) {
162 super(0, rs);
163 mFP = FieldPacker.createFromArray(args);
164
165 mArgs = args;
166 mBindings = globals;
167 mGlobalFuture = new HashMap<Script.FieldID, Future>();
168
169 int numValues = globals.size();
170
171 long[] fieldIDs = new long[numValues];
172 long[] values = new long[numValues];
173 int[] sizes = new int[numValues];
174 long[] depClosures = new long[numValues];
175 long[] depFieldIDs = new long[numValues];
176
177 int i = 0;
178 for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
179 Object obj = entry.getValue();
180 Script.FieldID fieldID = entry.getKey();
181 fieldIDs[i] = fieldID.getID(rs);
Yang Ni870767e2015-05-18 10:56:47 -0700182 retrieveValueAndDependenceInfo(rs, i, fieldID, obj, values,
183 sizes, depClosures, depFieldIDs);
Yang Ni18314ca2015-04-17 16:51:55 -0700184 i++;
185 }
186
187 long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs,
188 values, sizes);
189
190 setID(id);
Yang Ni6484b6b2016-03-24 09:40:32 -0700191
192 guard.open("destroy");
Yang Ni18314ca2015-04-17 16:51:55 -0700193 }
194
Yang Ni44d1b3b2016-04-04 17:09:11 -0700195 /**
196 * Destroys this Closure and the Allocation for its return value
197 */
198 public void destroy() {
199 super.destroy();
200 if (mReturnValue != null) {
201 mReturnValue.destroy();
202 }
203 }
204
205 protected void finalize() throws Throwable {
206 // Set null mReturnValue to avoid double-destroying it, in case its
207 // finalizer races ahead.
208 mReturnValue = null;
209 super.finalize();
210 }
211
Yang Ni870767e2015-05-18 10:56:47 -0700212 private void retrieveValueAndDependenceInfo(RenderScript rs,
213 int index, Script.FieldID fid, Object obj,
Yang Ni18314ca2015-04-17 16:51:55 -0700214 long[] values, int[] sizes,
215 long[] depClosures,
216 long[] depFieldIDs) {
217
218 if (obj instanceof Future) {
219 Future f = (Future)obj;
220 obj = f.getValue();
221 depClosures[index] = f.getClosure().getID(rs);
222 Script.FieldID fieldID = f.getFieldID();
223 depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0;
Yang Ni18314ca2015-04-17 16:51:55 -0700224 } else {
225 depClosures[index] = 0;
226 depFieldIDs[index] = 0;
227 }
228
Yang Ni870767e2015-05-18 10:56:47 -0700229 if (obj instanceof Input) {
230 Input unbound = (Input)obj;
231 if (index < mArgs.length) {
232 unbound.addReference(this, index);
233 } else {
234 unbound.addReference(this, fid);
235 }
236 values[index] = 0;
237 sizes[index] = 0;
238 } else {
239 ValueAndSize vs = new ValueAndSize(rs, obj);
240 values[index] = vs.value;
241 sizes[index] = vs.size;
242 }
Yang Ni18314ca2015-04-17 16:51:55 -0700243 }
244
245 /**
246 * Returns the future for the return value
247 *
248 * @return a future
249 */
250
251 public Future getReturn() {
252 if (mReturnFuture == null) {
253 mReturnFuture = new Future(this, null, mReturnValue);
254 }
255
256 return mReturnFuture;
257 }
258
259 /**
260 * Returns the future for a global variable
261 *
262 * @param field the field ID for the global variable
263 * @return a future
264 */
265
266 public Future getGlobal(Script.FieldID field) {
267 Future f = mGlobalFuture.get(field);
268
269 if (f == null) {
270 // If the field is not bound to this closure, this will return a future
271 // without an associated value (reference). So this is not working for
272 // cross-module (cross-script) linking in this case where a field not
273 // explicitly bound.
Yang Ni870767e2015-05-18 10:56:47 -0700274 Object obj = mBindings.get(field);
275 if (obj instanceof Future) {
276 obj = ((Future)obj).getValue();
277 }
278 f = new Future(this, field, obj);
Yang Ni18314ca2015-04-17 16:51:55 -0700279 mGlobalFuture.put(field, f);
280 }
281
282 return f;
283 }
284
285 void setArg(int index, Object obj) {
Yang Ni870767e2015-05-18 10:56:47 -0700286 if (obj instanceof Future) {
287 obj = ((Future)obj).getValue();
288 }
Yang Ni18314ca2015-04-17 16:51:55 -0700289 mArgs[index] = obj;
290 ValueAndSize vs = new ValueAndSize(mRS, obj);
291 mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size);
292 }
293
294 void setGlobal(Script.FieldID fieldID, Object obj) {
Yang Ni870767e2015-05-18 10:56:47 -0700295 if (obj instanceof Future) {
296 obj = ((Future)obj).getValue();
297 }
Yang Ni18314ca2015-04-17 16:51:55 -0700298 mBindings.put(fieldID, obj);
299 ValueAndSize vs = new ValueAndSize(mRS, obj);
300 mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size);
301 }
302
303 private static final class ValueAndSize {
304 public ValueAndSize(RenderScript rs, Object obj) {
305 if (obj instanceof Allocation) {
306 value = ((Allocation)obj).getID(rs);
Yang Ni263cc902015-11-10 13:27:04 -0800307 // Special value for size to tell the runtime and driver that
308 // the value is an Allocation
Yang Ni18314ca2015-04-17 16:51:55 -0700309 size = -1;
310 } else if (obj instanceof Boolean) {
311 value = ((Boolean)obj).booleanValue() ? 1 : 0;
312 size = 4;
313 } else if (obj instanceof Integer) {
314 value = ((Integer)obj).longValue();
315 size = 4;
316 } else if (obj instanceof Long) {
317 value = ((Long)obj).longValue();
318 size = 8;
319 } else if (obj instanceof Float) {
Yang Ni263cc902015-11-10 13:27:04 -0800320 value = Float.floatToRawIntBits(((Float)obj).floatValue());
Yang Ni18314ca2015-04-17 16:51:55 -0700321 size = 4;
322 } else if (obj instanceof Double) {
Yang Ni263cc902015-11-10 13:27:04 -0800323 value = Double.doubleToRawLongBits(((Double)obj).doubleValue());
Yang Ni18314ca2015-04-17 16:51:55 -0700324 size = 8;
325 }
326 }
327 public long value;
328 public int size;
329 }
330 }
331
332 /**
333 * An opaque class for futures
334 * <p>
335 * A future represents an output of a closure, either the return value of
336 * the function, or the value of a global variable written by the function.
337 * A future is created by calling the {@link Closure#getReturn} or
338 * {@link Closure#getGlobal} method.
339 */
340
341 public static final class Future {
342 Closure mClosure;
343 Script.FieldID mFieldID;
344 Object mValue;
345
346 Future(Closure closure, Script.FieldID fieldID, Object value) {
347 mClosure = closure;
348 mFieldID = fieldID;
349 mValue = value;
350 }
351
352 Closure getClosure() { return mClosure; }
353 Script.FieldID getFieldID() { return mFieldID; }
354 Object getValue() { return mValue; }
355 }
356
357 /**
358 * An opaque class for script group inputs
359 * <p>
360 * Created by calling the {@link Builder2#addInput} method. The value
361 * is assigned in {@link ScriptGroup#execute(Object...)} method as
362 * one of its arguments. Arguments to the execute method should be in
363 * the same order as intputs are added using the addInput method.
364 */
365
366 public static final class Input {
367 // Either mFieldID or mArgIndex should be set but not both.
368 List<Pair<Closure, Script.FieldID>> mFieldID;
369 // -1 means unset. Legal values are 0 .. n-1, where n is the number of
370 // arguments for the referencing closure.
371 List<Pair<Closure, Integer>> mArgIndex;
Yang Ni870767e2015-05-18 10:56:47 -0700372 Object mValue;
Yang Ni18314ca2015-04-17 16:51:55 -0700373
374 Input() {
375 mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>();
376 mArgIndex = new ArrayList<Pair<Closure, Integer>>();
377 }
378
379 void addReference(Closure closure, int index) {
380 mArgIndex.add(Pair.create(closure, Integer.valueOf(index)));
381 }
382
383 void addReference(Closure closure, Script.FieldID fieldID) {
384 mFieldID.add(Pair.create(closure, fieldID));
385 }
386
387 void set(Object value) {
Yang Ni870767e2015-05-18 10:56:47 -0700388 mValue = value;
Yang Ni18314ca2015-04-17 16:51:55 -0700389 for (Pair<Closure, Integer> p : mArgIndex) {
390 Closure closure = p.first;
391 int index = p.second.intValue();
392 closure.setArg(index, value);
393 }
394 for (Pair<Closure, Script.FieldID> p : mFieldID) {
395 Closure closure = p.first;
396 Script.FieldID fieldID = p.second;
397 closure.setGlobal(fieldID, value);
398 }
399 }
Yang Ni870767e2015-05-18 10:56:47 -0700400
401 Object get() { return mValue; }
Yang Ni18314ca2015-04-17 16:51:55 -0700402 }
403
404 private String mName;
405 private List<Closure> mClosures;
406 private List<Input> mInputs2;
407 private Future[] mOutputs2;
408
Tim Murray460a0492013-11-19 12:45:54 -0800409 ScriptGroup(long id, RenderScript rs) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700410 super(id, rs);
Yang Ni6484b6b2016-03-24 09:40:32 -0700411 guard.open("destroy");
Jason Sams423ebcb2012-08-10 15:40:53 -0700412 }
413
Yang Ni18314ca2015-04-17 16:51:55 -0700414 ScriptGroup(RenderScript rs, String name, List<Closure> closures,
415 List<Input> inputs, Future[] outputs) {
416 super(0, rs);
417 mName = name;
418 mClosures = closures;
419 mInputs2 = inputs;
420 mOutputs2 = outputs;
421
422 long[] closureIDs = new long[closures.size()];
423 for (int i = 0; i < closureIDs.length; i++) {
424 closureIDs[i] = closures.get(i).getID(rs);
425 }
Yang Ni15fcf612016-03-10 16:12:31 -0800426 long id = rs.nScriptGroup2Create(name, RenderScript.getCachePath(), closureIDs);
Yang Ni18314ca2015-04-17 16:51:55 -0700427 setID(id);
Yang Ni6484b6b2016-03-24 09:40:32 -0700428 guard.open("destroy");
Yang Ni18314ca2015-04-17 16:51:55 -0700429 }
430
431 /**
432 * Executes a script group
433 *
Yang Ni43563892015-05-12 13:53:38 -0700434 * @param inputs Values for inputs to the script group, in the order as the
435 * inputs are added via {@link Builder2#addInput}.
436 * @return Outputs of the script group as an array of objects, in the order
437 * as futures are passed to {@link Builder2#create}.
Yang Ni18314ca2015-04-17 16:51:55 -0700438 */
439
440 public Object[] execute(Object... inputs) {
441 if (inputs.length < mInputs2.size()) {
442 Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
443 "less than expected " + mInputs2.size());
444 return null;
445 }
446
447 if (inputs.length > mInputs2.size()) {
448 Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
449 "more than expected " + mInputs2.size());
450 }
451
452 for (int i = 0; i < mInputs2.size(); i++) {
453 Object obj = inputs[i];
454 if (obj instanceof Future || obj instanceof Input) {
455 Log.e(TAG, this.toString() + ": input " + i +
456 " is a future or unbound value");
457 return null;
458 }
459 Input unbound = mInputs2.get(i);
460 unbound.set(obj);
461 }
462
463 mRS.nScriptGroup2Execute(getID(mRS));
464
465 Object[] outputObjs = new Object[mOutputs2.length];
466 int i = 0;
467 for (Future f : mOutputs2) {
Yang Ni870767e2015-05-18 10:56:47 -0700468 Object output = f.getValue();
469 if (output instanceof Input) {
470 output = ((Input)output).get();
471 }
472 outputObjs[i++] = output;
Yang Ni18314ca2015-04-17 16:51:55 -0700473 }
474 return outputObjs;
475 }
476
Jason Sams08a81582012-09-18 12:32:10 -0700477 /**
478 * Sets an input of the ScriptGroup. This specifies an
Tim Murrayc11e25c2013-04-09 11:01:01 -0700479 * Allocation to be used for kernels that require an input
480 * Allocation provided from outside of the ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -0700481 *
Yang Ni18314ca2015-04-17 16:51:55 -0700482 * @deprecated Set arguments to {@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 setInput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700489 for (int ct=0; ct < mInputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700490 if (mInputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700491 mInputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700492 mRS.nScriptGroupSetInput(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 * Sets an output of the ScriptGroup. This specifies an
Tim Murrayc11e25c2013-04-09 11:01:01 -0700501 * Allocation to be used for the kernels that require an output
502 * Allocation visible after the ScriptGroup is executed.
Jason Sams08a81582012-09-18 12:32:10 -0700503 *
Yang Ni18314ca2015-04-17 16:51:55 -0700504 * @deprecated Use return value of {@link #execute(Object...)} instead.
505 *
Jason Sams08a81582012-09-18 12:32:10 -0700506 * @param s The ID of the kernel where the allocation should be
507 * connected.
508 * @param a The allocation to connect.
509 */
510 public void setOutput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700511 for (int ct=0; ct < mOutputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700512 if (mOutputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700513 mOutputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700514 mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
Jason Sams423ebcb2012-08-10 15:40:53 -0700515 return;
516 }
517 }
518 throw new RSIllegalArgumentException("Script not found");
519 }
520
Jason Sams08a81582012-09-18 12:32:10 -0700521 /**
522 * Execute the ScriptGroup. This will run all the kernels in
Tim Murrayc11e25c2013-04-09 11:01:01 -0700523 * the ScriptGroup. No internal connection results will be visible
524 * after execution of the ScriptGroup.
Yang Ni18314ca2015-04-17 16:51:55 -0700525 *
526 * @deprecated Use {@link #execute} instead.
527 *
Jason Sams08a81582012-09-18 12:32:10 -0700528 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700529 public void execute() {
Jason Sams08a81582012-09-18 12:32:10 -0700530 mRS.nScriptGroupExecute(getID(mRS));
Jason Sams423ebcb2012-08-10 15:40:53 -0700531 }
532
533
Jason Sams08a81582012-09-18 12:32:10 -0700534 /**
Tim Murrayc11e25c2013-04-09 11:01:01 -0700535 * Helper class to build a ScriptGroup. A ScriptGroup is
536 * created in two steps.
Jason Sams08a81582012-09-18 12:32:10 -0700537 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700538 * First, all kernels to be used by the ScriptGroup should be added.
Jason Sams08a81582012-09-18 12:32:10 -0700539 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700540 * Second, add connections between kernels. There are two types
541 * of connections: kernel to kernel and kernel to field.
542 * Kernel to kernel allows a kernel's output to be passed to
543 * another kernel as input. Kernel to field allows the output of
544 * one kernel to be bound as a script global. Kernel to kernel is
545 * higher performance and should be used where possible.
Jason Sams08a81582012-09-18 12:32:10 -0700546 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700547 * A ScriptGroup must contain a single directed acyclic graph (DAG); it
548 * cannot contain cycles. Currently, all kernels used in a ScriptGroup
549 * must come from different Script objects. Additionally, all kernels
550 * in a ScriptGroup must have at least one input, output, or internal
551 * connection.
552 * <p>
553 * Once all connections are made, a call to {@link #create} will
Jason Sams08a81582012-09-18 12:32:10 -0700554 * return the ScriptGroup object.
555 *
Yang Ni18314ca2015-04-17 16:51:55 -0700556 * @deprecated Use {@link Builder2} instead.
557 *
Jason Sams08a81582012-09-18 12:32:10 -0700558 */
559 public static final class Builder {
560 private RenderScript mRS;
561 private ArrayList<Node> mNodes = new ArrayList<Node>();
562 private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>();
563 private int mKernelCount;
Jason Sams423ebcb2012-08-10 15:40:53 -0700564
Jason Sams08a81582012-09-18 12:32:10 -0700565 /**
Tim Murrayc11e25c2013-04-09 11:01:01 -0700566 * Create a Builder for generating a ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -0700567 *
568 *
Tim Murrayc11e25c2013-04-09 11:01:01 -0700569 * @param rs The RenderScript context.
Jason Sams08a81582012-09-18 12:32:10 -0700570 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700571 public Builder(RenderScript rs) {
572 mRS = rs;
573 }
574
Tim Murray091f7cc2012-10-12 12:02:18 -0700575 // do a DFS from original node, looking for original node
576 // any cycle that could be created must contain original node
577 private void validateCycle(Node target, Node original) {
578 for (int ct = 0; ct < target.mOutputs.size(); ct++) {
579 final ConnectLine cl = target.mOutputs.get(ct);
Jason Sams08a81582012-09-18 12:32:10 -0700580 if (cl.mToK != null) {
581 Node tn = findNode(cl.mToK.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700582 if (tn.equals(original)) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700583 throw new RSInvalidStateException("Loops in group not allowed.");
584 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700585 validateCycle(tn, original);
Jason Sams08a81582012-09-18 12:32:10 -0700586 }
587 if (cl.mToF != null) {
588 Node tn = findNode(cl.mToF.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700589 if (tn.equals(original)) {
Jason Sams08a81582012-09-18 12:32:10 -0700590 throw new RSInvalidStateException("Loops in group not allowed.");
591 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700592 validateCycle(tn, original);
Tim Murray2a603892012-10-10 14:21:46 -0700593 }
594 }
595 }
596
597 private void mergeDAGs(int valueUsed, int valueKilled) {
598 for (int ct=0; ct < mNodes.size(); ct++) {
599 if (mNodes.get(ct).dagNumber == valueKilled)
600 mNodes.get(ct).dagNumber = valueUsed;
601 }
602 }
603
604 private void validateDAGRecurse(Node n, int dagNumber) {
605 // combine DAGs if this node has been seen already
606 if (n.dagNumber != 0 && n.dagNumber != dagNumber) {
607 mergeDAGs(n.dagNumber, dagNumber);
608 return;
609 }
610
611 n.dagNumber = dagNumber;
612 for (int ct=0; ct < n.mOutputs.size(); ct++) {
613 final ConnectLine cl = n.mOutputs.get(ct);
614 if (cl.mToK != null) {
615 Node tn = findNode(cl.mToK.mScript);
616 validateDAGRecurse(tn, dagNumber);
617 }
618 if (cl.mToF != null) {
619 Node tn = findNode(cl.mToF.mScript);
620 validateDAGRecurse(tn, dagNumber);
621 }
622 }
623 }
624
625 private void validateDAG() {
626 for (int ct=0; ct < mNodes.size(); ct++) {
627 Node n = mNodes.get(ct);
628 if (n.mInputs.size() == 0) {
629 if (n.mOutputs.size() == 0 && mNodes.size() > 1) {
Yang Ni870767e2015-05-18 10:56:47 -0700630 String msg = "Groups cannot contain unconnected scripts";
631 throw new RSInvalidStateException(msg);
Tim Murray2a603892012-10-10 14:21:46 -0700632 }
633 validateDAGRecurse(n, ct+1);
634 }
635 }
636 int dagNumber = mNodes.get(0).dagNumber;
637 for (int ct=0; ct < mNodes.size(); ct++) {
638 if (mNodes.get(ct).dagNumber != dagNumber) {
639 throw new RSInvalidStateException("Multiple DAGs in group not allowed.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700640 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700641 }
642 }
643
Jason Sams08a81582012-09-18 12:32:10 -0700644 private Node findNode(Script s) {
645 for (int ct=0; ct < mNodes.size(); ct++) {
646 if (s == mNodes.get(ct).mScript) {
647 return mNodes.get(ct);
Jason Sams423ebcb2012-08-10 15:40:53 -0700648 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700649 }
650 return null;
651 }
652
Jason Sams08a81582012-09-18 12:32:10 -0700653 private Node findNode(Script.KernelID k) {
654 for (int ct=0; ct < mNodes.size(); ct++) {
655 Node n = mNodes.get(ct);
656 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
657 if (k == n.mKernels.get(ct2)) {
658 return n;
Jason Sams423ebcb2012-08-10 15:40:53 -0700659 }
660 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700661 }
Jason Sams08a81582012-09-18 12:32:10 -0700662 return null;
663 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700664
Jason Sams08a81582012-09-18 12:32:10 -0700665 /**
666 * Adds a Kernel to the group.
667 *
668 *
669 * @param k The kernel to add.
670 *
671 * @return Builder Returns this.
672 */
673 public Builder addKernel(Script.KernelID k) {
674 if (mLines.size() != 0) {
675 throw new RSInvalidStateException(
676 "Kernels may not be added once connections exist.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700677 }
Jason Sams08a81582012-09-18 12:32:10 -0700678
679 //android.util.Log.v("RSR", "addKernel 1 k=" + k);
680 if (findNode(k) != null) {
681 return this;
682 }
683 //android.util.Log.v("RSR", "addKernel 2 ");
684 mKernelCount++;
685 Node n = findNode(k.mScript);
686 if (n == null) {
687 //android.util.Log.v("RSR", "addKernel 3 ");
688 n = new Node(k.mScript);
689 mNodes.add(n);
690 }
691 n.mKernels.add(k);
692 return this;
693 }
694
695 /**
696 * Adds a connection to the group.
697 *
698 *
699 * @param t The type of the connection. This is used to
700 * determine the kernel launch sizes on the source side
701 * of this connection.
702 * @param from The source for the connection.
703 * @param to The destination of the connection.
704 *
705 * @return Builder Returns this
706 */
707 public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
708 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
709
710 Node nf = findNode(from);
711 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700712 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700713 }
714
715 Node nt = findNode(to.mScript);
716 if (nt == null) {
717 throw new RSInvalidStateException("To script not found.");
718 }
719
720 ConnectLine cl = new ConnectLine(t, from, to);
721 mLines.add(new ConnectLine(t, from, to));
722
723 nf.mOutputs.add(cl);
724 nt.mInputs.add(cl);
Jason Sams423ebcb2012-08-10 15:40:53 -0700725
Tim Murray091f7cc2012-10-12 12:02:18 -0700726 validateCycle(nf, nf);
Jason Sams423ebcb2012-08-10 15:40:53 -0700727 return this;
728 }
729
Jason Sams08a81582012-09-18 12:32:10 -0700730 /**
731 * Adds a connection to the group.
732 *
733 *
734 * @param t The type of the connection. This is used to
735 * determine the kernel launch sizes for both sides of
736 * this connection.
737 * @param from The source for the connection.
738 * @param to The destination of the connection.
739 *
740 * @return Builder Returns this
741 */
742 public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
743 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
744
745 Node nf = findNode(from);
746 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700747 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700748 }
749
750 Node nt = findNode(to);
751 if (nt == null) {
752 throw new RSInvalidStateException("To script not found.");
753 }
754
755 ConnectLine cl = new ConnectLine(t, from, to);
756 mLines.add(new ConnectLine(t, from, to));
757
758 nf.mOutputs.add(cl);
759 nt.mInputs.add(cl);
760
Tim Murray091f7cc2012-10-12 12:02:18 -0700761 validateCycle(nf, nf);
Jason Sams08a81582012-09-18 12:32:10 -0700762 return this;
763 }
764
765
766
767 /**
768 * Creates the Script group.
769 *
770 *
771 * @return ScriptGroup The new ScriptGroup
772 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700773 public ScriptGroup create() {
Tim Murray2a603892012-10-10 14:21:46 -0700774
775 if (mNodes.size() == 0) {
776 throw new RSInvalidStateException("Empty script groups are not allowed");
777 }
778
779 // reset DAG numbers in case we're building a second group
780 for (int ct=0; ct < mNodes.size(); ct++) {
781 mNodes.get(ct).dagNumber = 0;
782 }
783 validateDAG();
784
Jason Sams08a81582012-09-18 12:32:10 -0700785 ArrayList<IO> inputs = new ArrayList<IO>();
786 ArrayList<IO> outputs = new ArrayList<IO>();
Jason Sams423ebcb2012-08-10 15:40:53 -0700787
Ashok Bhat98071552014-02-12 09:54:43 +0000788 long[] kernels = new long[mKernelCount];
Jason Sams08a81582012-09-18 12:32:10 -0700789 int idx = 0;
790 for (int ct=0; ct < mNodes.size(); ct++) {
791 Node n = mNodes.get(ct);
792 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
793 final Script.KernelID kid = n.mKernels.get(ct2);
Ashok Bhat98071552014-02-12 09:54:43 +0000794 kernels[idx++] = kid.getID(mRS);
Jason Sams423ebcb2012-08-10 15:40:53 -0700795
Jason Sams08a81582012-09-18 12:32:10 -0700796 boolean hasInput = false;
797 boolean hasOutput = false;
798 for (int ct3=0; ct3 < n.mInputs.size(); ct3++) {
799 if (n.mInputs.get(ct3).mToK == kid) {
800 hasInput = true;
801 }
802 }
803 for (int ct3=0; ct3 < n.mOutputs.size(); ct3++) {
804 if (n.mOutputs.get(ct3).mFrom == kid) {
805 hasOutput = true;
806 }
807 }
808 if (!hasInput) {
809 inputs.add(new IO(kid));
810 }
811 if (!hasOutput) {
812 outputs.add(new IO(kid));
813 }
814
815 }
816 }
817 if (idx != mKernelCount) {
818 throw new RSRuntimeException("Count mismatch, should not happen.");
819 }
820
Ashok Bhat98071552014-02-12 09:54:43 +0000821 long[] src = new long[mLines.size()];
822 long[] dstk = new long[mLines.size()];
823 long[] dstf = new long[mLines.size()];
824 long[] types = new long[mLines.size()];
Jason Sams08a81582012-09-18 12:32:10 -0700825
826 for (int ct=0; ct < mLines.size(); ct++) {
827 ConnectLine cl = mLines.get(ct);
Ashok Bhat98071552014-02-12 09:54:43 +0000828 src[ct] = cl.mFrom.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700829 if (cl.mToK != null) {
Ashok Bhat98071552014-02-12 09:54:43 +0000830 dstk[ct] = cl.mToK.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700831 }
832 if (cl.mToF != null) {
Ashok Bhat98071552014-02-12 09:54:43 +0000833 dstf[ct] = cl.mToF.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700834 }
Ashok Bhat98071552014-02-12 09:54:43 +0000835 types[ct] = cl.mAllocationType.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700836 }
837
Tim Murray460a0492013-11-19 12:45:54 -0800838 long id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
Jason Sams08a81582012-09-18 12:32:10 -0700839 if (id == 0) {
840 throw new RSRuntimeException("Object creation error, should not happen.");
841 }
842
843 ScriptGroup sg = new ScriptGroup(id, mRS);
844 sg.mOutputs = new IO[outputs.size()];
845 for (int ct=0; ct < outputs.size(); ct++) {
846 sg.mOutputs[ct] = outputs.get(ct);
847 }
848
849 sg.mInputs = new IO[inputs.size()];
850 for (int ct=0; ct < inputs.size(); ct++) {
851 sg.mInputs[ct] = inputs.get(ct);
852 }
853
Jason Sams423ebcb2012-08-10 15:40:53 -0700854 return sg;
855 }
856
857 }
858
Yang Ni18314ca2015-04-17 16:51:55 -0700859 /**
860 * Represents a binding of a value to a global variable in a
861 * kernel or invocable function. Used in closure creation.
862 */
863
864 public static final class Binding {
865 private final Script.FieldID mField;
866 private final Object mValue;
867
868 /**
869 * Returns a Binding object that binds value to field
870 *
871 * @param field the Script.FieldID of the global variable
872 * @param value the value
873 */
874
875 public Binding(Script.FieldID field, Object value) {
876 mField = field;
877 mValue = value;
878 }
879
880 /**
881 * Returns the field ID
882 */
883
Yang Ni77eba482015-05-15 09:56:49 -0700884 Script.FieldID getField() { return mField; }
Yang Ni18314ca2015-04-17 16:51:55 -0700885
886 /**
887 * Returns the value
888 */
889
Yang Ni77eba482015-05-15 09:56:49 -0700890 Object getValue() { return mValue; }
Yang Ni18314ca2015-04-17 16:51:55 -0700891 }
892
893 /**
894 * The builder class for creating script groups
895 * <p>
896 * A script group is created using closures (see class {@link Closure}).
897 * A closure is a function call to a kernel or
898 * invocable function. Each function argument or global variable accessed inside
899 * the function is bound to 1) a known value, 2) a script group input
900 * (see class {@link Input}), or 3) a
901 * future (see class {@link Future}).
902 * A future is the output of a closure, either the return value of the
903 * function or a global variable written by that function.
904 * <p>
905 * Closures are created using the {@link #addKernel} or {@link #addInvoke}
906 * methods.
907 * When a closure is created, futures from previously created closures
908 * can be used as its inputs.
909 * External script group inputs can be used as inputs to individual closures as well.
910 * An external script group input is created using the {@link #addInput} method.
911 * A script group is created by a call to the {@link #create} method, which
912 * accepts an array of futures as the outputs for the script group.
913 * <p>
914 * Closures in a script group can be evaluated in any order as long as the
915 * following conditions are met:
916 * 1) a closure must be evaluated before any other closures that take its
917 * futures as inputs;
918 * 2) all closures added before an invoke closure must be evaluated
919 * before it;
920 * and 3) all closures added after an invoke closure must be evaluated after
921 * it.
922 * As a special case, the order that the closures are added is a legal
923 * evaluation order. However, other evaluation orders are possible, including
924 * concurrently evaluating independent closures.
925 */
926
927 public static final class Builder2 {
928 RenderScript mRS;
929 List<Closure> mClosures;
930 List<Input> mInputs;
931 private static final String TAG = "ScriptGroup.Builder2";
932
933 /**
934 * Returns a Builder object
935 *
936 * @param rs the RenderScript context
937 */
938 public Builder2(RenderScript rs) {
939 mRS = rs;
940 mClosures = new ArrayList<Closure>();
941 mInputs = new ArrayList<Input>();
942 }
943
944 /**
945 * Adds a closure for a kernel
946 *
947 * @param k Kernel ID for the kernel function
948 * @param returnType Allocation type for the return value
949 * @param args arguments to the kernel function
950 * @param globalBindings bindings for global variables
951 * @return a closure
952 */
953
954 private Closure addKernelInternal(Script.KernelID k, Type returnType, Object[] args,
955 Map<Script.FieldID, Object> globalBindings) {
956 Closure c = new Closure(mRS, k, returnType, args, globalBindings);
957 mClosures.add(c);
958 return c;
959 }
960
961 /**
962 * Adds a closure for an invocable function
963 *
964 * @param invoke Invoke ID for the invocable function
965 * @param args arguments to the invocable function
966 * @param globalBindings bindings for global variables
967 * @return a closure
968 */
969
970 private Closure addInvokeInternal(Script.InvokeID invoke, Object[] args,
971 Map<Script.FieldID, Object> globalBindings) {
972 Closure c = new Closure(mRS, invoke, args, globalBindings);
973 mClosures.add(c);
974 return c;
975 }
976
977 /**
978 * Adds a script group input
979 *
980 * @return a script group input, which can be used as an argument or a value to
981 * a global variable for creating closures
982 */
983 public Input addInput() {
984 Input unbound = new Input();
985 mInputs.add(unbound);
986 return unbound;
987 }
988
989 /**
990 * Adds a closure for a kernel
991 *
992 * @param k Kernel ID for the kernel function
993 * @param argsAndBindings arguments followed by bindings for global variables
994 * @return a closure
995 */
996
997 public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) {
998 ArrayList<Object> args = new ArrayList<Object>();
999 Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
1000 if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
1001 return null;
1002 }
1003 return addKernelInternal(k, returnType, args.toArray(), bindingMap);
1004 }
1005
1006 /**
1007 * Adds a closure for an invocable function
1008 *
1009 * @param invoke Invoke ID for the invocable function
1010 * @param argsAndBindings arguments followed by bindings for global variables
1011 * @return a closure
1012 */
1013
1014 public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) {
1015 ArrayList<Object> args = new ArrayList<Object>();
1016 Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
1017 if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
1018 return null;
1019 }
1020 return addInvokeInternal(invoke, args.toArray(), bindingMap);
1021 }
1022
1023 /**
1024 * Creates a script group
1025 *
1026 * @param name name for the script group. Legal names can only contain letters, digits,
1027 * '-', or '_'. The name can be no longer than 100 characters.
Yang Nidbb6fd52015-06-24 17:06:10 -07001028 * Try to use unique names, to avoid name conflicts and reduce
1029 * the cost of group creation.
Yang Ni18314ca2015-04-17 16:51:55 -07001030 * @param outputs futures intended as outputs of the script group
1031 * @return a script group
1032 */
1033
1034 public ScriptGroup create(String name, Future... outputs) {
1035 if (name == null || name.isEmpty() || name.length() > 100 ||
1036 !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) {
1037 throw new RSIllegalArgumentException("invalid script group name");
1038 }
1039 ScriptGroup ret = new ScriptGroup(mRS, name, mClosures, mInputs, outputs);
Yang Ni44d1b3b2016-04-04 17:09:11 -07001040 mClosures = new ArrayList<Closure>();
1041 mInputs = new ArrayList<Input>();
Yang Ni18314ca2015-04-17 16:51:55 -07001042 return ret;
1043 }
1044
1045 private boolean seperateArgsAndBindings(Object[] argsAndBindings,
1046 ArrayList<Object> args,
1047 Map<Script.FieldID, Object> bindingMap) {
1048 int i;
1049 for (i = 0; i < argsAndBindings.length; i++) {
1050 if (argsAndBindings[i] instanceof Binding) {
1051 break;
1052 }
1053 args.add(argsAndBindings[i]);
1054 }
1055
1056 for (; i < argsAndBindings.length; i++) {
1057 if (!(argsAndBindings[i] instanceof Binding)) {
1058 return false;
1059 }
1060 Binding b = (Binding)argsAndBindings[i];
1061 bindingMap.put(b.getField(), b.getValue());
1062 }
1063
1064 return true;
1065 }
1066
1067 }
Jason Sams423ebcb2012-08-10 15:40:53 -07001068
Yang Ni44d1b3b2016-04-04 17:09:11 -07001069 /**
1070 * Destroy this ScriptGroup and all Closures in it
1071 */
1072 public void destroy() {
1073 super.destroy();
Yang Ni1b4df162016-04-20 21:26:24 -07001074 // ScriptGroup created using the old Builder class does not
1075 // initialize the field mClosures
Yang Ni07837d62016-04-18 14:27:02 -07001076 if (mClosures != null) {
Yang Ni1b4df162016-04-20 21:26:24 -07001077 for (Closure c : mClosures) {
1078 c.destroy();
1079 }
Yang Ni07837d62016-04-18 14:27:02 -07001080 }
Yang Ni44d1b3b2016-04-04 17:09:11 -07001081 }
Jason Sams423ebcb2012-08-10 15:40:53 -07001082}