blob: 7afdb39763988515a0470c460117f6c3cf6ae2a8 [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
19import java.lang.reflect.Method;
Jason Sams08a81582012-09-18 12:32:10 -070020import java.util.ArrayList;
Jason Sams423ebcb2012-08-10 15:40:53 -070021
22/**
Jason Sams08a81582012-09-18 12:32:10 -070023 * ScriptGroup creates a groups of scripts which are executed
24 * together based upon upon one execution call as if they were
25 * all part of a single script. The scripts may be connected
26 * internally or to an external allocation. For the internal
27 * connections the intermediate results are not observable after
28 * the execution of the script.
29 * <p>
30 * The external connections are grouped into inputs and outputs.
31 * All outputs are produced by a script kernel and placed into a
32 * user supplied allocation. Inputs are similar but supply the
33 * input of a kernal. Inputs bounds to a script are set directly
34 * upon the script.
Tim Murray2a603892012-10-10 14:21:46 -070035 * <p>
36 * A ScriptGroup must contain at least one kernel. A ScriptGroup
37 * must contain only a single directed acyclic graph (DAG) of
38 * script kernels and connections. Attempting to create a
39 * ScriptGroup with multiple DAGs or attempting to create
40 * a cycle within a ScriptGroup will throw an exception.
Jason Sams08a81582012-09-18 12:32:10 -070041 *
Jason Sams423ebcb2012-08-10 15:40:53 -070042 **/
Jason Sams08a81582012-09-18 12:32:10 -070043public final class ScriptGroup extends BaseObj {
Jason Sams423ebcb2012-08-10 15:40:53 -070044 IO mOutputs[];
45 IO mInputs[];
46
47 static class IO {
Jason Sams08a81582012-09-18 12:32:10 -070048 Script.KernelID mKID;
Jason Sams423ebcb2012-08-10 15:40:53 -070049 Allocation mAllocation;
Jason Sams423ebcb2012-08-10 15:40:53 -070050
Jason Sams08a81582012-09-18 12:32:10 -070051 IO(Script.KernelID s) {
52 mKID = s;
Jason Sams423ebcb2012-08-10 15:40:53 -070053 }
54 }
55
Jason Sams08a81582012-09-18 12:32:10 -070056 static class ConnectLine {
57 ConnectLine(Type t, Script.KernelID from, Script.KernelID to) {
58 mFrom = from;
59 mToK = to;
Jason Sams423ebcb2012-08-10 15:40:53 -070060 mAllocationType = t;
61 }
62
Jason Sams08a81582012-09-18 12:32:10 -070063 ConnectLine(Type t, Script.KernelID from, Script.FieldID to) {
64 mFrom = from;
65 mToF = to;
66 mAllocationType = t;
Jason Sams423ebcb2012-08-10 15:40:53 -070067 }
Jason Sams08a81582012-09-18 12:32:10 -070068
69 Script.FieldID mToF;
70 Script.KernelID mToK;
71 Script.KernelID mFrom;
72 Type mAllocationType;
Jason Sams423ebcb2012-08-10 15:40:53 -070073 }
74
75 static class Node {
76 Script mScript;
Jason Sams08a81582012-09-18 12:32:10 -070077 ArrayList<Script.KernelID> mKernels = new ArrayList<Script.KernelID>();
78 ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>();
79 ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>();
Tim Murray2a603892012-10-10 14:21:46 -070080 int dagNumber;
Jason Sams423ebcb2012-08-10 15:40:53 -070081
82 Node mNext;
83
84 Node(Script s) {
85 mScript = s;
86 }
Jason Sams423ebcb2012-08-10 15:40:53 -070087 }
88
89
90 ScriptGroup(int id, RenderScript rs) {
91 super(id, rs);
92 }
93
Jason Sams08a81582012-09-18 12:32:10 -070094 /**
95 * Sets an input of the ScriptGroup. This specifies an
96 * Allocation to be used for the kernels which require a kernel
97 * input and that input is provided external to the group.
98 *
99 * @param s The ID of the kernel where the allocation should be
100 * connected.
101 * @param a The allocation to connect.
102 */
103 public void setInput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700104 for (int ct=0; ct < mInputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700105 if (mInputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700106 mInputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700107 mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a));
Jason Sams423ebcb2012-08-10 15:40:53 -0700108 return;
109 }
110 }
111 throw new RSIllegalArgumentException("Script not found");
112 }
113
Jason Sams08a81582012-09-18 12:32:10 -0700114 /**
115 * Sets an output of the ScriptGroup. This specifies an
116 * Allocation to be used for the kernels which require a kernel
117 * output and that output is provided external to the group.
118 *
119 * @param s The ID of the kernel where the allocation should be
120 * connected.
121 * @param a The allocation to connect.
122 */
123 public void setOutput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700124 for (int ct=0; ct < mOutputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700125 if (mOutputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700126 mOutputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700127 mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
Jason Sams423ebcb2012-08-10 15:40:53 -0700128 return;
129 }
130 }
131 throw new RSIllegalArgumentException("Script not found");
132 }
133
Jason Sams08a81582012-09-18 12:32:10 -0700134 /**
135 * Execute the ScriptGroup. This will run all the kernels in
136 * the script. The state of the connecting lines will not be
137 * observable after this operation.
138 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700139 public void execute() {
Jason Sams08a81582012-09-18 12:32:10 -0700140 mRS.nScriptGroupExecute(getID(mRS));
Jason Sams423ebcb2012-08-10 15:40:53 -0700141 }
142
143
Jason Sams08a81582012-09-18 12:32:10 -0700144 /**
145 * Create a ScriptGroup. There are two steps to creating a
146 * ScriptGoup.
147 * <p>
148 * First all the Kernels to be used by the group should be
149 * added. Once this is done the kernels should be connected.
150 * Kernels cannot be added once a connection has been made.
151 * <p>
152 * Second, add connections. There are two forms of connections.
153 * Kernel to Kernel and Kernel to Field. Kernel to Kernel is
154 * higher performance and should be used where possible. The
155 * line of connections cannot form a loop. If a loop is detected
156 * an exception is thrown.
157 * <p>
158 * Once all the connections are made a call to create will
159 * return the ScriptGroup object.
160 *
161 */
162 public static final class Builder {
163 private RenderScript mRS;
164 private ArrayList<Node> mNodes = new ArrayList<Node>();
165 private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>();
166 private int mKernelCount;
Jason Sams423ebcb2012-08-10 15:40:53 -0700167
Jason Sams08a81582012-09-18 12:32:10 -0700168 /**
169 * Create a builder for generating a ScriptGroup.
170 *
171 *
172 * @param rs The Renderscript context.
173 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700174 public Builder(RenderScript rs) {
175 mRS = rs;
176 }
177
Tim Murray091f7cc2012-10-12 12:02:18 -0700178 // do a DFS from original node, looking for original node
179 // any cycle that could be created must contain original node
180 private void validateCycle(Node target, Node original) {
181 for (int ct = 0; ct < target.mOutputs.size(); ct++) {
182 final ConnectLine cl = target.mOutputs.get(ct);
Jason Sams08a81582012-09-18 12:32:10 -0700183 if (cl.mToK != null) {
184 Node tn = findNode(cl.mToK.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700185 if (tn.equals(original)) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700186 throw new RSInvalidStateException("Loops in group not allowed.");
187 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700188 validateCycle(tn, original);
Jason Sams08a81582012-09-18 12:32:10 -0700189 }
190 if (cl.mToF != null) {
191 Node tn = findNode(cl.mToF.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700192 if (tn.equals(original)) {
Jason Sams08a81582012-09-18 12:32:10 -0700193 throw new RSInvalidStateException("Loops in group not allowed.");
194 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700195 validateCycle(tn, original);
Tim Murray2a603892012-10-10 14:21:46 -0700196 }
197 }
198 }
199
200 private void mergeDAGs(int valueUsed, int valueKilled) {
201 for (int ct=0; ct < mNodes.size(); ct++) {
202 if (mNodes.get(ct).dagNumber == valueKilled)
203 mNodes.get(ct).dagNumber = valueUsed;
204 }
205 }
206
207 private void validateDAGRecurse(Node n, int dagNumber) {
208 // combine DAGs if this node has been seen already
209 if (n.dagNumber != 0 && n.dagNumber != dagNumber) {
210 mergeDAGs(n.dagNumber, dagNumber);
211 return;
212 }
213
214 n.dagNumber = dagNumber;
215 for (int ct=0; ct < n.mOutputs.size(); ct++) {
216 final ConnectLine cl = n.mOutputs.get(ct);
217 if (cl.mToK != null) {
218 Node tn = findNode(cl.mToK.mScript);
219 validateDAGRecurse(tn, dagNumber);
220 }
221 if (cl.mToF != null) {
222 Node tn = findNode(cl.mToF.mScript);
223 validateDAGRecurse(tn, dagNumber);
224 }
225 }
226 }
227
228 private void validateDAG() {
229 for (int ct=0; ct < mNodes.size(); ct++) {
230 Node n = mNodes.get(ct);
231 if (n.mInputs.size() == 0) {
232 if (n.mOutputs.size() == 0 && mNodes.size() > 1) {
233 throw new RSInvalidStateException("Groups cannot contain unconnected scripts");
234 }
235 validateDAGRecurse(n, ct+1);
236 }
237 }
238 int dagNumber = mNodes.get(0).dagNumber;
239 for (int ct=0; ct < mNodes.size(); ct++) {
240 if (mNodes.get(ct).dagNumber != dagNumber) {
241 throw new RSInvalidStateException("Multiple DAGs in group not allowed.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700242 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700243 }
244 }
245
Jason Sams08a81582012-09-18 12:32:10 -0700246 private Node findNode(Script s) {
247 for (int ct=0; ct < mNodes.size(); ct++) {
248 if (s == mNodes.get(ct).mScript) {
249 return mNodes.get(ct);
Jason Sams423ebcb2012-08-10 15:40:53 -0700250 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700251 }
252 return null;
253 }
254
Jason Sams08a81582012-09-18 12:32:10 -0700255 private Node findNode(Script.KernelID k) {
256 for (int ct=0; ct < mNodes.size(); ct++) {
257 Node n = mNodes.get(ct);
258 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
259 if (k == n.mKernels.get(ct2)) {
260 return n;
Jason Sams423ebcb2012-08-10 15:40:53 -0700261 }
262 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700263 }
Jason Sams08a81582012-09-18 12:32:10 -0700264 return null;
265 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700266
Jason Sams08a81582012-09-18 12:32:10 -0700267 /**
268 * Adds a Kernel to the group.
269 *
270 *
271 * @param k The kernel to add.
272 *
273 * @return Builder Returns this.
274 */
275 public Builder addKernel(Script.KernelID k) {
276 if (mLines.size() != 0) {
277 throw new RSInvalidStateException(
278 "Kernels may not be added once connections exist.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700279 }
Jason Sams08a81582012-09-18 12:32:10 -0700280
281 //android.util.Log.v("RSR", "addKernel 1 k=" + k);
282 if (findNode(k) != null) {
283 return this;
284 }
285 //android.util.Log.v("RSR", "addKernel 2 ");
286 mKernelCount++;
287 Node n = findNode(k.mScript);
288 if (n == null) {
289 //android.util.Log.v("RSR", "addKernel 3 ");
290 n = new Node(k.mScript);
291 mNodes.add(n);
292 }
293 n.mKernels.add(k);
294 return this;
295 }
296
297 /**
298 * Adds a connection to the group.
299 *
300 *
301 * @param t The type of the connection. This is used to
302 * determine the kernel launch sizes on the source side
303 * of this connection.
304 * @param from The source for the connection.
305 * @param to The destination of the connection.
306 *
307 * @return Builder Returns this
308 */
309 public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
310 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
311
312 Node nf = findNode(from);
313 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700314 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700315 }
316
317 Node nt = findNode(to.mScript);
318 if (nt == null) {
319 throw new RSInvalidStateException("To script not found.");
320 }
321
322 ConnectLine cl = new ConnectLine(t, from, to);
323 mLines.add(new ConnectLine(t, from, to));
324
325 nf.mOutputs.add(cl);
326 nt.mInputs.add(cl);
Jason Sams423ebcb2012-08-10 15:40:53 -0700327
Tim Murray091f7cc2012-10-12 12:02:18 -0700328 validateCycle(nf, nf);
Jason Sams423ebcb2012-08-10 15:40:53 -0700329 return this;
330 }
331
Jason Sams08a81582012-09-18 12:32:10 -0700332 /**
333 * Adds a connection to the group.
334 *
335 *
336 * @param t The type of the connection. This is used to
337 * determine the kernel launch sizes for both sides of
338 * this connection.
339 * @param from The source for the connection.
340 * @param to The destination of the connection.
341 *
342 * @return Builder Returns this
343 */
344 public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
345 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
346
347 Node nf = findNode(from);
348 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700349 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700350 }
351
352 Node nt = findNode(to);
353 if (nt == null) {
354 throw new RSInvalidStateException("To script not found.");
355 }
356
357 ConnectLine cl = new ConnectLine(t, from, to);
358 mLines.add(new ConnectLine(t, from, to));
359
360 nf.mOutputs.add(cl);
361 nt.mInputs.add(cl);
362
Tim Murray091f7cc2012-10-12 12:02:18 -0700363 validateCycle(nf, nf);
Jason Sams08a81582012-09-18 12:32:10 -0700364 return this;
365 }
366
367
368
369 /**
370 * Creates the Script group.
371 *
372 *
373 * @return ScriptGroup The new ScriptGroup
374 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700375 public ScriptGroup create() {
Tim Murray2a603892012-10-10 14:21:46 -0700376
377 if (mNodes.size() == 0) {
378 throw new RSInvalidStateException("Empty script groups are not allowed");
379 }
380
381 // reset DAG numbers in case we're building a second group
382 for (int ct=0; ct < mNodes.size(); ct++) {
383 mNodes.get(ct).dagNumber = 0;
384 }
385 validateDAG();
386
Jason Sams08a81582012-09-18 12:32:10 -0700387 ArrayList<IO> inputs = new ArrayList<IO>();
388 ArrayList<IO> outputs = new ArrayList<IO>();
Jason Sams423ebcb2012-08-10 15:40:53 -0700389
Jason Sams08a81582012-09-18 12:32:10 -0700390 int[] kernels = new int[mKernelCount];
391 int idx = 0;
392 for (int ct=0; ct < mNodes.size(); ct++) {
393 Node n = mNodes.get(ct);
394 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
395 final Script.KernelID kid = n.mKernels.get(ct2);
396 kernels[idx++] = kid.getID(mRS);
Jason Sams423ebcb2012-08-10 15:40:53 -0700397
Jason Sams08a81582012-09-18 12:32:10 -0700398 boolean hasInput = false;
399 boolean hasOutput = false;
400 for (int ct3=0; ct3 < n.mInputs.size(); ct3++) {
401 if (n.mInputs.get(ct3).mToK == kid) {
402 hasInput = true;
403 }
404 }
405 for (int ct3=0; ct3 < n.mOutputs.size(); ct3++) {
406 if (n.mOutputs.get(ct3).mFrom == kid) {
407 hasOutput = true;
408 }
409 }
410 if (!hasInput) {
411 inputs.add(new IO(kid));
412 }
413 if (!hasOutput) {
414 outputs.add(new IO(kid));
415 }
416
417 }
418 }
419 if (idx != mKernelCount) {
420 throw new RSRuntimeException("Count mismatch, should not happen.");
421 }
422
423 int[] src = new int[mLines.size()];
424 int[] dstk = new int[mLines.size()];
425 int[] dstf = new int[mLines.size()];
426 int[] types = new int[mLines.size()];
427
428 for (int ct=0; ct < mLines.size(); ct++) {
429 ConnectLine cl = mLines.get(ct);
430 src[ct] = cl.mFrom.getID(mRS);
431 if (cl.mToK != null) {
432 dstk[ct] = cl.mToK.getID(mRS);
433 }
434 if (cl.mToF != null) {
435 dstf[ct] = cl.mToF.getID(mRS);
436 }
437 types[ct] = cl.mAllocationType.getID(mRS);
438 }
439
440 int id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
441 if (id == 0) {
442 throw new RSRuntimeException("Object creation error, should not happen.");
443 }
444
445 ScriptGroup sg = new ScriptGroup(id, mRS);
446 sg.mOutputs = new IO[outputs.size()];
447 for (int ct=0; ct < outputs.size(); ct++) {
448 sg.mOutputs[ct] = outputs.get(ct);
449 }
450
451 sg.mInputs = new IO[inputs.size()];
452 for (int ct=0; ct < inputs.size(); ct++) {
453 sg.mInputs[ct] = inputs.get(ct);
454 }
455
Jason Sams423ebcb2012-08-10 15:40:53 -0700456 return sg;
457 }
458
459 }
460
461
462}
463
464