blob: f1a72732600bbee65bc49b3cb9f8ff8ed2b4081f [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
Jason Sams08a81582012-09-18 12:32:10 -070019import java.util.ArrayList;
Jason Sams423ebcb2012-08-10 15:40:53 -070020
21/**
Tim Murrayc11e25c2013-04-09 11:01:01 -070022 * ScriptGroup creates a group of kernels that are executed
23 * together with one execution call as if they were a single kernel.
24 * The kernels may be connected internally or to an external allocation.
25 * The intermediate results for internal connections are not observable
26 * after the execution of the script.
Jason Sams08a81582012-09-18 12:32:10 -070027 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -070028 * External connections are grouped into inputs and outputs.
Jason Sams08a81582012-09-18 12:32:10 -070029 * All outputs are produced by a script kernel and placed into a
Tim Murrayc11e25c2013-04-09 11:01:01 -070030 * user-supplied allocation. Inputs provide the input of a kernel.
31 * Inputs bound to script globals are set directly upon the script.
Tim Murray2a603892012-10-10 14:21:46 -070032 * <p>
33 * A ScriptGroup must contain at least one kernel. A ScriptGroup
34 * must contain only a single directed acyclic graph (DAG) of
35 * script kernels and connections. Attempting to create a
36 * ScriptGroup with multiple DAGs or attempting to create
37 * a cycle within a ScriptGroup will throw an exception.
Tim Murrayc11e25c2013-04-09 11:01:01 -070038 * <p>
39 * Currently, all kernels in a ScriptGroup must be from separate
40 * Script objects. Attempting to use multiple kernels from the same
41 * Script object will result in an {@link android.renderscript.RSInvalidStateException}.
Jason Sams08a81582012-09-18 12:32:10 -070042 *
Jason Sams423ebcb2012-08-10 15:40:53 -070043 **/
Jason Sams08a81582012-09-18 12:32:10 -070044public final class ScriptGroup extends BaseObj {
Jason Sams423ebcb2012-08-10 15:40:53 -070045 IO mOutputs[];
46 IO mInputs[];
47
48 static class IO {
Jason Sams08a81582012-09-18 12:32:10 -070049 Script.KernelID mKID;
Jason Sams423ebcb2012-08-10 15:40:53 -070050 Allocation mAllocation;
Jason Sams423ebcb2012-08-10 15:40:53 -070051
Jason Sams08a81582012-09-18 12:32:10 -070052 IO(Script.KernelID s) {
53 mKID = s;
Jason Sams423ebcb2012-08-10 15:40:53 -070054 }
55 }
56
Jason Sams08a81582012-09-18 12:32:10 -070057 static class ConnectLine {
58 ConnectLine(Type t, Script.KernelID from, Script.KernelID to) {
59 mFrom = from;
60 mToK = to;
Jason Sams423ebcb2012-08-10 15:40:53 -070061 mAllocationType = t;
62 }
63
Jason Sams08a81582012-09-18 12:32:10 -070064 ConnectLine(Type t, Script.KernelID from, Script.FieldID to) {
65 mFrom = from;
66 mToF = to;
67 mAllocationType = t;
Jason Sams423ebcb2012-08-10 15:40:53 -070068 }
Jason Sams08a81582012-09-18 12:32:10 -070069
70 Script.FieldID mToF;
71 Script.KernelID mToK;
72 Script.KernelID mFrom;
73 Type mAllocationType;
Jason Sams423ebcb2012-08-10 15:40:53 -070074 }
75
76 static class Node {
77 Script mScript;
Jason Sams08a81582012-09-18 12:32:10 -070078 ArrayList<Script.KernelID> mKernels = new ArrayList<Script.KernelID>();
79 ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>();
80 ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>();
Tim Murray2a603892012-10-10 14:21:46 -070081 int dagNumber;
Jason Sams423ebcb2012-08-10 15:40:53 -070082
83 Node mNext;
84
85 Node(Script s) {
86 mScript = s;
87 }
Jason Sams423ebcb2012-08-10 15:40:53 -070088 }
89
90
Tim Murray460a0492013-11-19 12:45:54 -080091 ScriptGroup(long id, RenderScript rs) {
Jason Sams423ebcb2012-08-10 15:40:53 -070092 super(id, rs);
93 }
94
Jason Sams08a81582012-09-18 12:32:10 -070095 /**
96 * Sets an input of the ScriptGroup. This specifies an
Tim Murrayc11e25c2013-04-09 11:01:01 -070097 * Allocation to be used for kernels that require an input
98 * Allocation provided from outside of the ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -070099 *
100 * @param s The ID of the kernel where the allocation should be
101 * connected.
102 * @param a The allocation to connect.
103 */
104 public void setInput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700105 for (int ct=0; ct < mInputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700106 if (mInputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700107 mInputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700108 mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a));
Jason Sams423ebcb2012-08-10 15:40:53 -0700109 return;
110 }
111 }
112 throw new RSIllegalArgumentException("Script not found");
113 }
114
Jason Sams08a81582012-09-18 12:32:10 -0700115 /**
116 * Sets an output of the ScriptGroup. This specifies an
Tim Murrayc11e25c2013-04-09 11:01:01 -0700117 * Allocation to be used for the kernels that require an output
118 * Allocation visible after the ScriptGroup is executed.
Jason Sams08a81582012-09-18 12:32:10 -0700119 *
120 * @param s The ID of the kernel where the allocation should be
121 * connected.
122 * @param a The allocation to connect.
123 */
124 public void setOutput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700125 for (int ct=0; ct < mOutputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700126 if (mOutputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700127 mOutputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700128 mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
Jason Sams423ebcb2012-08-10 15:40:53 -0700129 return;
130 }
131 }
132 throw new RSIllegalArgumentException("Script not found");
133 }
134
Jason Sams08a81582012-09-18 12:32:10 -0700135 /**
136 * Execute the ScriptGroup. This will run all the kernels in
Tim Murrayc11e25c2013-04-09 11:01:01 -0700137 * the ScriptGroup. No internal connection results will be visible
138 * after execution of the ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -0700139 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700140 public void execute() {
Jason Sams08a81582012-09-18 12:32:10 -0700141 mRS.nScriptGroupExecute(getID(mRS));
Jason Sams423ebcb2012-08-10 15:40:53 -0700142 }
143
144
Jason Sams08a81582012-09-18 12:32:10 -0700145 /**
Tim Murrayc11e25c2013-04-09 11:01:01 -0700146 * Helper class to build a ScriptGroup. A ScriptGroup is
147 * created in two steps.
Jason Sams08a81582012-09-18 12:32:10 -0700148 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700149 * First, all kernels to be used by the ScriptGroup should be added.
Jason Sams08a81582012-09-18 12:32:10 -0700150 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700151 * Second, add connections between kernels. There are two types
152 * of connections: kernel to kernel and kernel to field.
153 * Kernel to kernel allows a kernel's output to be passed to
154 * another kernel as input. Kernel to field allows the output of
155 * one kernel to be bound as a script global. Kernel to kernel is
156 * higher performance and should be used where possible.
Jason Sams08a81582012-09-18 12:32:10 -0700157 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700158 * A ScriptGroup must contain a single directed acyclic graph (DAG); it
159 * cannot contain cycles. Currently, all kernels used in a ScriptGroup
160 * must come from different Script objects. Additionally, all kernels
161 * in a ScriptGroup must have at least one input, output, or internal
162 * connection.
163 * <p>
164 * Once all connections are made, a call to {@link #create} will
Jason Sams08a81582012-09-18 12:32:10 -0700165 * return the ScriptGroup object.
166 *
167 */
168 public static final class Builder {
169 private RenderScript mRS;
170 private ArrayList<Node> mNodes = new ArrayList<Node>();
171 private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>();
172 private int mKernelCount;
Jason Sams423ebcb2012-08-10 15:40:53 -0700173
Jason Sams08a81582012-09-18 12:32:10 -0700174 /**
Tim Murrayc11e25c2013-04-09 11:01:01 -0700175 * Create a Builder for generating a ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -0700176 *
177 *
Tim Murrayc11e25c2013-04-09 11:01:01 -0700178 * @param rs The RenderScript context.
Jason Sams08a81582012-09-18 12:32:10 -0700179 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700180 public Builder(RenderScript rs) {
181 mRS = rs;
182 }
183
Tim Murray091f7cc2012-10-12 12:02:18 -0700184 // do a DFS from original node, looking for original node
185 // any cycle that could be created must contain original node
186 private void validateCycle(Node target, Node original) {
187 for (int ct = 0; ct < target.mOutputs.size(); ct++) {
188 final ConnectLine cl = target.mOutputs.get(ct);
Jason Sams08a81582012-09-18 12:32:10 -0700189 if (cl.mToK != null) {
190 Node tn = findNode(cl.mToK.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700191 if (tn.equals(original)) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700192 throw new RSInvalidStateException("Loops in group not allowed.");
193 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700194 validateCycle(tn, original);
Jason Sams08a81582012-09-18 12:32:10 -0700195 }
196 if (cl.mToF != null) {
197 Node tn = findNode(cl.mToF.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700198 if (tn.equals(original)) {
Jason Sams08a81582012-09-18 12:32:10 -0700199 throw new RSInvalidStateException("Loops in group not allowed.");
200 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700201 validateCycle(tn, original);
Tim Murray2a603892012-10-10 14:21:46 -0700202 }
203 }
204 }
205
206 private void mergeDAGs(int valueUsed, int valueKilled) {
207 for (int ct=0; ct < mNodes.size(); ct++) {
208 if (mNodes.get(ct).dagNumber == valueKilled)
209 mNodes.get(ct).dagNumber = valueUsed;
210 }
211 }
212
213 private void validateDAGRecurse(Node n, int dagNumber) {
214 // combine DAGs if this node has been seen already
215 if (n.dagNumber != 0 && n.dagNumber != dagNumber) {
216 mergeDAGs(n.dagNumber, dagNumber);
217 return;
218 }
219
220 n.dagNumber = dagNumber;
221 for (int ct=0; ct < n.mOutputs.size(); ct++) {
222 final ConnectLine cl = n.mOutputs.get(ct);
223 if (cl.mToK != null) {
224 Node tn = findNode(cl.mToK.mScript);
225 validateDAGRecurse(tn, dagNumber);
226 }
227 if (cl.mToF != null) {
228 Node tn = findNode(cl.mToF.mScript);
229 validateDAGRecurse(tn, dagNumber);
230 }
231 }
232 }
233
234 private void validateDAG() {
235 for (int ct=0; ct < mNodes.size(); ct++) {
236 Node n = mNodes.get(ct);
237 if (n.mInputs.size() == 0) {
238 if (n.mOutputs.size() == 0 && mNodes.size() > 1) {
239 throw new RSInvalidStateException("Groups cannot contain unconnected scripts");
240 }
241 validateDAGRecurse(n, ct+1);
242 }
243 }
244 int dagNumber = mNodes.get(0).dagNumber;
245 for (int ct=0; ct < mNodes.size(); ct++) {
246 if (mNodes.get(ct).dagNumber != dagNumber) {
247 throw new RSInvalidStateException("Multiple DAGs in group not allowed.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700248 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700249 }
250 }
251
Jason Sams08a81582012-09-18 12:32:10 -0700252 private Node findNode(Script s) {
253 for (int ct=0; ct < mNodes.size(); ct++) {
254 if (s == mNodes.get(ct).mScript) {
255 return mNodes.get(ct);
Jason Sams423ebcb2012-08-10 15:40:53 -0700256 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700257 }
258 return null;
259 }
260
Jason Sams08a81582012-09-18 12:32:10 -0700261 private Node findNode(Script.KernelID k) {
262 for (int ct=0; ct < mNodes.size(); ct++) {
263 Node n = mNodes.get(ct);
264 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
265 if (k == n.mKernels.get(ct2)) {
266 return n;
Jason Sams423ebcb2012-08-10 15:40:53 -0700267 }
268 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700269 }
Jason Sams08a81582012-09-18 12:32:10 -0700270 return null;
271 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700272
Jason Sams08a81582012-09-18 12:32:10 -0700273 /**
274 * Adds a Kernel to the group.
275 *
276 *
277 * @param k The kernel to add.
278 *
279 * @return Builder Returns this.
280 */
281 public Builder addKernel(Script.KernelID k) {
282 if (mLines.size() != 0) {
283 throw new RSInvalidStateException(
284 "Kernels may not be added once connections exist.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700285 }
Jason Sams08a81582012-09-18 12:32:10 -0700286
287 //android.util.Log.v("RSR", "addKernel 1 k=" + k);
288 if (findNode(k) != null) {
289 return this;
290 }
291 //android.util.Log.v("RSR", "addKernel 2 ");
292 mKernelCount++;
293 Node n = findNode(k.mScript);
294 if (n == null) {
295 //android.util.Log.v("RSR", "addKernel 3 ");
296 n = new Node(k.mScript);
297 mNodes.add(n);
298 }
299 n.mKernels.add(k);
300 return this;
301 }
302
303 /**
304 * Adds a connection to the group.
305 *
306 *
307 * @param t The type of the connection. This is used to
308 * determine the kernel launch sizes on the source side
309 * of this connection.
310 * @param from The source for the connection.
311 * @param to The destination of the connection.
312 *
313 * @return Builder Returns this
314 */
315 public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
316 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
317
318 Node nf = findNode(from);
319 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700320 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700321 }
322
323 Node nt = findNode(to.mScript);
324 if (nt == null) {
325 throw new RSInvalidStateException("To script not found.");
326 }
327
328 ConnectLine cl = new ConnectLine(t, from, to);
329 mLines.add(new ConnectLine(t, from, to));
330
331 nf.mOutputs.add(cl);
332 nt.mInputs.add(cl);
Jason Sams423ebcb2012-08-10 15:40:53 -0700333
Tim Murray091f7cc2012-10-12 12:02:18 -0700334 validateCycle(nf, nf);
Jason Sams423ebcb2012-08-10 15:40:53 -0700335 return this;
336 }
337
Jason Sams08a81582012-09-18 12:32:10 -0700338 /**
339 * Adds a connection to the group.
340 *
341 *
342 * @param t The type of the connection. This is used to
343 * determine the kernel launch sizes for both sides of
344 * this connection.
345 * @param from The source for the connection.
346 * @param to The destination of the connection.
347 *
348 * @return Builder Returns this
349 */
350 public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
351 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
352
353 Node nf = findNode(from);
354 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700355 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700356 }
357
358 Node nt = findNode(to);
359 if (nt == null) {
360 throw new RSInvalidStateException("To script not found.");
361 }
362
363 ConnectLine cl = new ConnectLine(t, from, to);
364 mLines.add(new ConnectLine(t, from, to));
365
366 nf.mOutputs.add(cl);
367 nt.mInputs.add(cl);
368
Tim Murray091f7cc2012-10-12 12:02:18 -0700369 validateCycle(nf, nf);
Jason Sams08a81582012-09-18 12:32:10 -0700370 return this;
371 }
372
373
374
375 /**
376 * Creates the Script group.
377 *
378 *
379 * @return ScriptGroup The new ScriptGroup
380 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700381 public ScriptGroup create() {
Tim Murray460a0492013-11-19 12:45:54 -0800382 // FIXME: this is broken for 64-bit
Tim Murray2a603892012-10-10 14:21:46 -0700383
384 if (mNodes.size() == 0) {
385 throw new RSInvalidStateException("Empty script groups are not allowed");
386 }
387
388 // reset DAG numbers in case we're building a second group
389 for (int ct=0; ct < mNodes.size(); ct++) {
390 mNodes.get(ct).dagNumber = 0;
391 }
392 validateDAG();
393
Jason Sams08a81582012-09-18 12:32:10 -0700394 ArrayList<IO> inputs = new ArrayList<IO>();
395 ArrayList<IO> outputs = new ArrayList<IO>();
Jason Sams423ebcb2012-08-10 15:40:53 -0700396
Jason Sams08a81582012-09-18 12:32:10 -0700397 int[] kernels = new int[mKernelCount];
398 int idx = 0;
399 for (int ct=0; ct < mNodes.size(); ct++) {
400 Node n = mNodes.get(ct);
401 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
402 final Script.KernelID kid = n.mKernels.get(ct2);
Tim Murray460a0492013-11-19 12:45:54 -0800403 kernels[idx++] = (int)kid.getID(mRS);
Jason Sams423ebcb2012-08-10 15:40:53 -0700404
Jason Sams08a81582012-09-18 12:32:10 -0700405 boolean hasInput = false;
406 boolean hasOutput = false;
407 for (int ct3=0; ct3 < n.mInputs.size(); ct3++) {
408 if (n.mInputs.get(ct3).mToK == kid) {
409 hasInput = true;
410 }
411 }
412 for (int ct3=0; ct3 < n.mOutputs.size(); ct3++) {
413 if (n.mOutputs.get(ct3).mFrom == kid) {
414 hasOutput = true;
415 }
416 }
417 if (!hasInput) {
418 inputs.add(new IO(kid));
419 }
420 if (!hasOutput) {
421 outputs.add(new IO(kid));
422 }
423
424 }
425 }
426 if (idx != mKernelCount) {
427 throw new RSRuntimeException("Count mismatch, should not happen.");
428 }
429
430 int[] src = new int[mLines.size()];
431 int[] dstk = new int[mLines.size()];
432 int[] dstf = new int[mLines.size()];
433 int[] types = new int[mLines.size()];
434
435 for (int ct=0; ct < mLines.size(); ct++) {
436 ConnectLine cl = mLines.get(ct);
Tim Murray460a0492013-11-19 12:45:54 -0800437 src[ct] = (int)cl.mFrom.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700438 if (cl.mToK != null) {
Tim Murray460a0492013-11-19 12:45:54 -0800439 dstk[ct] = (int)cl.mToK.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700440 }
441 if (cl.mToF != null) {
Tim Murray460a0492013-11-19 12:45:54 -0800442 dstf[ct] = (int)cl.mToF.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700443 }
Tim Murray460a0492013-11-19 12:45:54 -0800444 types[ct] = (int)cl.mAllocationType.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700445 }
446
Tim Murray460a0492013-11-19 12:45:54 -0800447 long id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
Jason Sams08a81582012-09-18 12:32:10 -0700448 if (id == 0) {
449 throw new RSRuntimeException("Object creation error, should not happen.");
450 }
451
452 ScriptGroup sg = new ScriptGroup(id, mRS);
453 sg.mOutputs = new IO[outputs.size()];
454 for (int ct=0; ct < outputs.size(); ct++) {
455 sg.mOutputs[ct] = outputs.get(ct);
456 }
457
458 sg.mInputs = new IO[inputs.size()];
459 for (int ct=0; ct < inputs.size(); ct++) {
460 sg.mInputs[ct] = inputs.get(ct);
461 }
462
Jason Sams423ebcb2012-08-10 15:40:53 -0700463 return sg;
464 }
465
466 }
467
468
469}
470
471