1/* 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 android.util.Log; 20import android.util.Pair; 21import java.util.ArrayList; 22import java.util.HashMap; 23import java.util.List; 24import java.util.Map; 25 26/** 27 * A group of kernels that are executed 28 * together with one execution call as if they were a single kernel 29 * <p> 30 * 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. 36 * <p> 37 * 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. 40 **/ 41public final class ScriptGroup extends BaseObj { 42 private static final String TAG = "ScriptGroup"; 43 IO mOutputs[]; 44 IO mInputs[]; 45 46 static class IO { 47 Script.KernelID mKID; 48 Allocation mAllocation; 49 50 IO(Script.KernelID s) { 51 mKID = s; 52 } 53 } 54 55 static class ConnectLine { 56 ConnectLine(Type t, Script.KernelID from, Script.KernelID to) { 57 mFrom = from; 58 mToK = to; 59 mAllocationType = t; 60 } 61 62 ConnectLine(Type t, Script.KernelID from, Script.FieldID to) { 63 mFrom = from; 64 mToF = to; 65 mAllocationType = t; 66 } 67 68 Script.FieldID mToF; 69 Script.KernelID mToK; 70 Script.KernelID mFrom; 71 Type mAllocationType; 72 } 73 74 static class Node { 75 Script mScript; 76 ArrayList<Script.KernelID> mKernels = new ArrayList<Script.KernelID>(); 77 ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>(); 78 ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>(); 79 int dagNumber; 80 81 Node mNext; 82 83 Node(Script s) { 84 mScript = s; 85 } 86 } 87 88 89 /** 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++) { 134 fieldIDs[i] = 0; 135 retrieveValueAndDependenceInfo(rs, i, null, args[i], 136 values, sizes, depClosures, depFieldIDs); 137 } 138 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); 142 retrieveValueAndDependenceInfo(rs, i, fieldID, obj, 143 values, sizes, depClosures, depFieldIDs); 144 i++; 145 } 146 147 long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs), 148 fieldIDs, values, sizes, depClosures, depFieldIDs); 149 150 setID(id); 151 } 152 153 Closure(RenderScript rs, Script.InvokeID invokeID, 154 Object[] args, Map<Script.FieldID, Object> globals) { 155 super(0, rs); 156 mFP = FieldPacker.createFromArray(args); 157 158 mArgs = args; 159 mBindings = globals; 160 mGlobalFuture = new HashMap<Script.FieldID, Future>(); 161 162 int numValues = globals.size(); 163 164 long[] fieldIDs = new long[numValues]; 165 long[] values = new long[numValues]; 166 int[] sizes = new int[numValues]; 167 long[] depClosures = new long[numValues]; 168 long[] depFieldIDs = new long[numValues]; 169 170 int i = 0; 171 for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) { 172 Object obj = entry.getValue(); 173 Script.FieldID fieldID = entry.getKey(); 174 fieldIDs[i] = fieldID.getID(rs); 175 retrieveValueAndDependenceInfo(rs, i, fieldID, obj, values, 176 sizes, depClosures, depFieldIDs); 177 i++; 178 } 179 180 long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs, 181 values, sizes); 182 183 setID(id); 184 } 185 186 private void retrieveValueAndDependenceInfo(RenderScript rs, 187 int index, Script.FieldID fid, Object obj, 188 long[] values, int[] sizes, 189 long[] depClosures, 190 long[] depFieldIDs) { 191 192 if (obj instanceof Future) { 193 Future f = (Future)obj; 194 obj = f.getValue(); 195 depClosures[index] = f.getClosure().getID(rs); 196 Script.FieldID fieldID = f.getFieldID(); 197 depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0; 198 } else { 199 depClosures[index] = 0; 200 depFieldIDs[index] = 0; 201 } 202 203 if (obj instanceof Input) { 204 Input unbound = (Input)obj; 205 if (index < mArgs.length) { 206 unbound.addReference(this, index); 207 } else { 208 unbound.addReference(this, fid); 209 } 210 values[index] = 0; 211 sizes[index] = 0; 212 } else { 213 ValueAndSize vs = new ValueAndSize(rs, obj); 214 values[index] = vs.value; 215 sizes[index] = vs.size; 216 } 217 } 218 219 /** 220 * Returns the future for the return value 221 * 222 * @return a future 223 */ 224 225 public Future getReturn() { 226 if (mReturnFuture == null) { 227 mReturnFuture = new Future(this, null, mReturnValue); 228 } 229 230 return mReturnFuture; 231 } 232 233 /** 234 * Returns the future for a global variable 235 * 236 * @param field the field ID for the global variable 237 * @return a future 238 */ 239 240 public Future getGlobal(Script.FieldID field) { 241 Future f = mGlobalFuture.get(field); 242 243 if (f == null) { 244 // If the field is not bound to this closure, this will return a future 245 // without an associated value (reference). So this is not working for 246 // cross-module (cross-script) linking in this case where a field not 247 // explicitly bound. 248 Object obj = mBindings.get(field); 249 if (obj instanceof Future) { 250 obj = ((Future)obj).getValue(); 251 } 252 f = new Future(this, field, obj); 253 mGlobalFuture.put(field, f); 254 } 255 256 return f; 257 } 258 259 void setArg(int index, Object obj) { 260 if (obj instanceof Future) { 261 obj = ((Future)obj).getValue(); 262 } 263 mArgs[index] = obj; 264 ValueAndSize vs = new ValueAndSize(mRS, obj); 265 mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size); 266 } 267 268 void setGlobal(Script.FieldID fieldID, Object obj) { 269 if (obj instanceof Future) { 270 obj = ((Future)obj).getValue(); 271 } 272 mBindings.put(fieldID, obj); 273 ValueAndSize vs = new ValueAndSize(mRS, obj); 274 mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size); 275 } 276 277 private static final class ValueAndSize { 278 public ValueAndSize(RenderScript rs, Object obj) { 279 if (obj instanceof Allocation) { 280 value = ((Allocation)obj).getID(rs); 281 size = -1; 282 } else if (obj instanceof Boolean) { 283 value = ((Boolean)obj).booleanValue() ? 1 : 0; 284 size = 4; 285 } else if (obj instanceof Integer) { 286 value = ((Integer)obj).longValue(); 287 size = 4; 288 } else if (obj instanceof Long) { 289 value = ((Long)obj).longValue(); 290 size = 8; 291 } else if (obj instanceof Float) { 292 value = ((Float)obj).longValue(); 293 size = 4; 294 } else if (obj instanceof Double) { 295 value = ((Double)obj).longValue(); 296 size = 8; 297 } 298 } 299 public long value; 300 public int size; 301 } 302 } 303 304 /** 305 * An opaque class for futures 306 * <p> 307 * A future represents an output of a closure, either the return value of 308 * the function, or the value of a global variable written by the function. 309 * A future is created by calling the {@link Closure#getReturn} or 310 * {@link Closure#getGlobal} method. 311 */ 312 313 public static final class Future { 314 Closure mClosure; 315 Script.FieldID mFieldID; 316 Object mValue; 317 318 Future(Closure closure, Script.FieldID fieldID, Object value) { 319 mClosure = closure; 320 mFieldID = fieldID; 321 mValue = value; 322 } 323 324 Closure getClosure() { return mClosure; } 325 Script.FieldID getFieldID() { return mFieldID; } 326 Object getValue() { return mValue; } 327 } 328 329 /** 330 * An opaque class for script group inputs 331 * <p> 332 * Created by calling the {@link Builder2#addInput} method. The value 333 * is assigned in {@link ScriptGroup#execute(Object...)} method as 334 * one of its arguments. Arguments to the execute method should be in 335 * the same order as intputs are added using the addInput method. 336 */ 337 338 public static final class Input { 339 // Either mFieldID or mArgIndex should be set but not both. 340 List<Pair<Closure, Script.FieldID>> mFieldID; 341 // -1 means unset. Legal values are 0 .. n-1, where n is the number of 342 // arguments for the referencing closure. 343 List<Pair<Closure, Integer>> mArgIndex; 344 Object mValue; 345 346 Input() { 347 mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>(); 348 mArgIndex = new ArrayList<Pair<Closure, Integer>>(); 349 } 350 351 void addReference(Closure closure, int index) { 352 mArgIndex.add(Pair.create(closure, Integer.valueOf(index))); 353 } 354 355 void addReference(Closure closure, Script.FieldID fieldID) { 356 mFieldID.add(Pair.create(closure, fieldID)); 357 } 358 359 void set(Object value) { 360 mValue = value; 361 for (Pair<Closure, Integer> p : mArgIndex) { 362 Closure closure = p.first; 363 int index = p.second.intValue(); 364 closure.setArg(index, value); 365 } 366 for (Pair<Closure, Script.FieldID> p : mFieldID) { 367 Closure closure = p.first; 368 Script.FieldID fieldID = p.second; 369 closure.setGlobal(fieldID, value); 370 } 371 } 372 373 Object get() { return mValue; } 374 } 375 376 private String mName; 377 private List<Closure> mClosures; 378 private List<Input> mInputs2; 379 private Future[] mOutputs2; 380 381 ScriptGroup(long id, RenderScript rs) { 382 super(id, rs); 383 } 384 385 ScriptGroup(RenderScript rs, String name, List<Closure> closures, 386 List<Input> inputs, Future[] outputs) { 387 super(0, rs); 388 mName = name; 389 mClosures = closures; 390 mInputs2 = inputs; 391 mOutputs2 = outputs; 392 393 long[] closureIDs = new long[closures.size()]; 394 for (int i = 0; i < closureIDs.length; i++) { 395 closureIDs[i] = closures.get(i).getID(rs); 396 } 397 long id = rs.nScriptGroup2Create(name, ScriptC.mCachePath, closureIDs); 398 setID(id); 399 } 400 401 /** 402 * Executes a script group 403 * 404 * @param inputs Values for inputs to the script group, in the order as the 405 * inputs are added via {@link Builder2#addInput}. 406 * @return Outputs of the script group as an array of objects, in the order 407 * as futures are passed to {@link Builder2#create}. 408 */ 409 410 public Object[] execute(Object... inputs) { 411 if (inputs.length < mInputs2.size()) { 412 Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " + 413 "less than expected " + mInputs2.size()); 414 return null; 415 } 416 417 if (inputs.length > mInputs2.size()) { 418 Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " + 419 "more than expected " + mInputs2.size()); 420 } 421 422 for (int i = 0; i < mInputs2.size(); i++) { 423 Object obj = inputs[i]; 424 if (obj instanceof Future || obj instanceof Input) { 425 Log.e(TAG, this.toString() + ": input " + i + 426 " is a future or unbound value"); 427 return null; 428 } 429 Input unbound = mInputs2.get(i); 430 unbound.set(obj); 431 } 432 433 mRS.nScriptGroup2Execute(getID(mRS)); 434 435 Object[] outputObjs = new Object[mOutputs2.length]; 436 int i = 0; 437 for (Future f : mOutputs2) { 438 Object output = f.getValue(); 439 if (output instanceof Input) { 440 output = ((Input)output).get(); 441 } 442 outputObjs[i++] = output; 443 } 444 return outputObjs; 445 } 446 447 /** 448 * Sets an input of the ScriptGroup. This specifies an 449 * Allocation to be used for kernels that require an input 450 * Allocation provided from outside of the ScriptGroup. 451 * 452 * @deprecated Set arguments to {@link #execute(Object...)} instead. 453 * 454 * @param s The ID of the kernel where the allocation should be 455 * connected. 456 * @param a The allocation to connect. 457 */ 458 public void setInput(Script.KernelID s, Allocation a) { 459 for (int ct=0; ct < mInputs.length; ct++) { 460 if (mInputs[ct].mKID == s) { 461 mInputs[ct].mAllocation = a; 462 mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a)); 463 return; 464 } 465 } 466 throw new RSIllegalArgumentException("Script not found"); 467 } 468 469 /** 470 * Sets an output of the ScriptGroup. This specifies an 471 * Allocation to be used for the kernels that require an output 472 * Allocation visible after the ScriptGroup is executed. 473 * 474 * @deprecated Use return value of {@link #execute(Object...)} instead. 475 * 476 * @param s The ID of the kernel where the allocation should be 477 * connected. 478 * @param a The allocation to connect. 479 */ 480 public void setOutput(Script.KernelID s, Allocation a) { 481 for (int ct=0; ct < mOutputs.length; ct++) { 482 if (mOutputs[ct].mKID == s) { 483 mOutputs[ct].mAllocation = a; 484 mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a)); 485 return; 486 } 487 } 488 throw new RSIllegalArgumentException("Script not found"); 489 } 490 491 /** 492 * Execute the ScriptGroup. This will run all the kernels in 493 * the ScriptGroup. No internal connection results will be visible 494 * after execution of the ScriptGroup. 495 * 496 * @deprecated Use {@link #execute} instead. 497 * 498 */ 499 public void execute() { 500 mRS.nScriptGroupExecute(getID(mRS)); 501 } 502 503 504 /** 505 * Helper class to build a ScriptGroup. A ScriptGroup is 506 * created in two steps. 507 * <p> 508 * First, all kernels to be used by the ScriptGroup should be added. 509 * <p> 510 * Second, add connections between kernels. There are two types 511 * of connections: kernel to kernel and kernel to field. 512 * Kernel to kernel allows a kernel's output to be passed to 513 * another kernel as input. Kernel to field allows the output of 514 * one kernel to be bound as a script global. Kernel to kernel is 515 * higher performance and should be used where possible. 516 * <p> 517 * A ScriptGroup must contain a single directed acyclic graph (DAG); it 518 * cannot contain cycles. Currently, all kernels used in a ScriptGroup 519 * must come from different Script objects. Additionally, all kernels 520 * in a ScriptGroup must have at least one input, output, or internal 521 * connection. 522 * <p> 523 * Once all connections are made, a call to {@link #create} will 524 * return the ScriptGroup object. 525 * 526 * @deprecated Use {@link Builder2} instead. 527 * 528 */ 529 public static final class Builder { 530 private RenderScript mRS; 531 private ArrayList<Node> mNodes = new ArrayList<Node>(); 532 private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>(); 533 private int mKernelCount; 534 535 /** 536 * Create a Builder for generating a ScriptGroup. 537 * 538 * 539 * @param rs The RenderScript context. 540 */ 541 public Builder(RenderScript rs) { 542 mRS = rs; 543 } 544 545 // do a DFS from original node, looking for original node 546 // any cycle that could be created must contain original node 547 private void validateCycle(Node target, Node original) { 548 for (int ct = 0; ct < target.mOutputs.size(); ct++) { 549 final ConnectLine cl = target.mOutputs.get(ct); 550 if (cl.mToK != null) { 551 Node tn = findNode(cl.mToK.mScript); 552 if (tn.equals(original)) { 553 throw new RSInvalidStateException("Loops in group not allowed."); 554 } 555 validateCycle(tn, original); 556 } 557 if (cl.mToF != null) { 558 Node tn = findNode(cl.mToF.mScript); 559 if (tn.equals(original)) { 560 throw new RSInvalidStateException("Loops in group not allowed."); 561 } 562 validateCycle(tn, original); 563 } 564 } 565 } 566 567 private void mergeDAGs(int valueUsed, int valueKilled) { 568 for (int ct=0; ct < mNodes.size(); ct++) { 569 if (mNodes.get(ct).dagNumber == valueKilled) 570 mNodes.get(ct).dagNumber = valueUsed; 571 } 572 } 573 574 private void validateDAGRecurse(Node n, int dagNumber) { 575 // combine DAGs if this node has been seen already 576 if (n.dagNumber != 0 && n.dagNumber != dagNumber) { 577 mergeDAGs(n.dagNumber, dagNumber); 578 return; 579 } 580 581 n.dagNumber = dagNumber; 582 for (int ct=0; ct < n.mOutputs.size(); ct++) { 583 final ConnectLine cl = n.mOutputs.get(ct); 584 if (cl.mToK != null) { 585 Node tn = findNode(cl.mToK.mScript); 586 validateDAGRecurse(tn, dagNumber); 587 } 588 if (cl.mToF != null) { 589 Node tn = findNode(cl.mToF.mScript); 590 validateDAGRecurse(tn, dagNumber); 591 } 592 } 593 } 594 595 private void validateDAG() { 596 for (int ct=0; ct < mNodes.size(); ct++) { 597 Node n = mNodes.get(ct); 598 if (n.mInputs.size() == 0) { 599 if (n.mOutputs.size() == 0 && mNodes.size() > 1) { 600 String msg = "Groups cannot contain unconnected scripts"; 601 throw new RSInvalidStateException(msg); 602 } 603 validateDAGRecurse(n, ct+1); 604 } 605 } 606 int dagNumber = mNodes.get(0).dagNumber; 607 for (int ct=0; ct < mNodes.size(); ct++) { 608 if (mNodes.get(ct).dagNumber != dagNumber) { 609 throw new RSInvalidStateException("Multiple DAGs in group not allowed."); 610 } 611 } 612 } 613 614 private Node findNode(Script s) { 615 for (int ct=0; ct < mNodes.size(); ct++) { 616 if (s == mNodes.get(ct).mScript) { 617 return mNodes.get(ct); 618 } 619 } 620 return null; 621 } 622 623 private Node findNode(Script.KernelID k) { 624 for (int ct=0; ct < mNodes.size(); ct++) { 625 Node n = mNodes.get(ct); 626 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) { 627 if (k == n.mKernels.get(ct2)) { 628 return n; 629 } 630 } 631 } 632 return null; 633 } 634 635 /** 636 * Adds a Kernel to the group. 637 * 638 * 639 * @param k The kernel to add. 640 * 641 * @return Builder Returns this. 642 */ 643 public Builder addKernel(Script.KernelID k) { 644 if (mLines.size() != 0) { 645 throw new RSInvalidStateException( 646 "Kernels may not be added once connections exist."); 647 } 648 649 //android.util.Log.v("RSR", "addKernel 1 k=" + k); 650 if (findNode(k) != null) { 651 return this; 652 } 653 //android.util.Log.v("RSR", "addKernel 2 "); 654 mKernelCount++; 655 Node n = findNode(k.mScript); 656 if (n == null) { 657 //android.util.Log.v("RSR", "addKernel 3 "); 658 n = new Node(k.mScript); 659 mNodes.add(n); 660 } 661 n.mKernels.add(k); 662 return this; 663 } 664 665 /** 666 * Adds a connection to the group. 667 * 668 * 669 * @param t The type of the connection. This is used to 670 * determine the kernel launch sizes on the source side 671 * of this connection. 672 * @param from The source for the connection. 673 * @param to The destination of the connection. 674 * 675 * @return Builder Returns this 676 */ 677 public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) { 678 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to); 679 680 Node nf = findNode(from); 681 if (nf == null) { 682 throw new RSInvalidStateException("From script not found."); 683 } 684 685 Node nt = findNode(to.mScript); 686 if (nt == null) { 687 throw new RSInvalidStateException("To script not found."); 688 } 689 690 ConnectLine cl = new ConnectLine(t, from, to); 691 mLines.add(new ConnectLine(t, from, to)); 692 693 nf.mOutputs.add(cl); 694 nt.mInputs.add(cl); 695 696 validateCycle(nf, nf); 697 return this; 698 } 699 700 /** 701 * Adds a connection to the group. 702 * 703 * 704 * @param t The type of the connection. This is used to 705 * determine the kernel launch sizes for both sides of 706 * this connection. 707 * @param from The source for the connection. 708 * @param to The destination of the connection. 709 * 710 * @return Builder Returns this 711 */ 712 public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) { 713 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to); 714 715 Node nf = findNode(from); 716 if (nf == null) { 717 throw new RSInvalidStateException("From script not found."); 718 } 719 720 Node nt = findNode(to); 721 if (nt == null) { 722 throw new RSInvalidStateException("To script not found."); 723 } 724 725 ConnectLine cl = new ConnectLine(t, from, to); 726 mLines.add(new ConnectLine(t, from, to)); 727 728 nf.mOutputs.add(cl); 729 nt.mInputs.add(cl); 730 731 validateCycle(nf, nf); 732 return this; 733 } 734 735 736 737 /** 738 * Creates the Script group. 739 * 740 * 741 * @return ScriptGroup The new ScriptGroup 742 */ 743 public ScriptGroup create() { 744 745 if (mNodes.size() == 0) { 746 throw new RSInvalidStateException("Empty script groups are not allowed"); 747 } 748 749 // reset DAG numbers in case we're building a second group 750 for (int ct=0; ct < mNodes.size(); ct++) { 751 mNodes.get(ct).dagNumber = 0; 752 } 753 validateDAG(); 754 755 ArrayList<IO> inputs = new ArrayList<IO>(); 756 ArrayList<IO> outputs = new ArrayList<IO>(); 757 758 long[] kernels = new long[mKernelCount]; 759 int idx = 0; 760 for (int ct=0; ct < mNodes.size(); ct++) { 761 Node n = mNodes.get(ct); 762 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) { 763 final Script.KernelID kid = n.mKernels.get(ct2); 764 kernels[idx++] = kid.getID(mRS); 765 766 boolean hasInput = false; 767 boolean hasOutput = false; 768 for (int ct3=0; ct3 < n.mInputs.size(); ct3++) { 769 if (n.mInputs.get(ct3).mToK == kid) { 770 hasInput = true; 771 } 772 } 773 for (int ct3=0; ct3 < n.mOutputs.size(); ct3++) { 774 if (n.mOutputs.get(ct3).mFrom == kid) { 775 hasOutput = true; 776 } 777 } 778 if (!hasInput) { 779 inputs.add(new IO(kid)); 780 } 781 if (!hasOutput) { 782 outputs.add(new IO(kid)); 783 } 784 785 } 786 } 787 if (idx != mKernelCount) { 788 throw new RSRuntimeException("Count mismatch, should not happen."); 789 } 790 791 long[] src = new long[mLines.size()]; 792 long[] dstk = new long[mLines.size()]; 793 long[] dstf = new long[mLines.size()]; 794 long[] types = new long[mLines.size()]; 795 796 for (int ct=0; ct < mLines.size(); ct++) { 797 ConnectLine cl = mLines.get(ct); 798 src[ct] = cl.mFrom.getID(mRS); 799 if (cl.mToK != null) { 800 dstk[ct] = cl.mToK.getID(mRS); 801 } 802 if (cl.mToF != null) { 803 dstf[ct] = cl.mToF.getID(mRS); 804 } 805 types[ct] = cl.mAllocationType.getID(mRS); 806 } 807 808 long id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types); 809 if (id == 0) { 810 throw new RSRuntimeException("Object creation error, should not happen."); 811 } 812 813 ScriptGroup sg = new ScriptGroup(id, mRS); 814 sg.mOutputs = new IO[outputs.size()]; 815 for (int ct=0; ct < outputs.size(); ct++) { 816 sg.mOutputs[ct] = outputs.get(ct); 817 } 818 819 sg.mInputs = new IO[inputs.size()]; 820 for (int ct=0; ct < inputs.size(); ct++) { 821 sg.mInputs[ct] = inputs.get(ct); 822 } 823 824 return sg; 825 } 826 827 } 828 829 /** 830 * Represents a binding of a value to a global variable in a 831 * kernel or invocable function. Used in closure creation. 832 */ 833 834 public static final class Binding { 835 private final Script.FieldID mField; 836 private final Object mValue; 837 838 /** 839 * Returns a Binding object that binds value to field 840 * 841 * @param field the Script.FieldID of the global variable 842 * @param value the value 843 */ 844 845 public Binding(Script.FieldID field, Object value) { 846 mField = field; 847 mValue = value; 848 } 849 850 /** 851 * Returns the field ID 852 */ 853 854 Script.FieldID getField() { return mField; } 855 856 /** 857 * Returns the value 858 */ 859 860 Object getValue() { return mValue; } 861 } 862 863 /** 864 * The builder class for creating script groups 865 * <p> 866 * A script group is created using closures (see class {@link Closure}). 867 * A closure is a function call to a kernel or 868 * invocable function. Each function argument or global variable accessed inside 869 * the function is bound to 1) a known value, 2) a script group input 870 * (see class {@link Input}), or 3) a 871 * future (see class {@link Future}). 872 * A future is the output of a closure, either the return value of the 873 * function or a global variable written by that function. 874 * <p> 875 * Closures are created using the {@link #addKernel} or {@link #addInvoke} 876 * methods. 877 * When a closure is created, futures from previously created closures 878 * can be used as its inputs. 879 * External script group inputs can be used as inputs to individual closures as well. 880 * An external script group input is created using the {@link #addInput} method. 881 * A script group is created by a call to the {@link #create} method, which 882 * accepts an array of futures as the outputs for the script group. 883 * <p> 884 * Closures in a script group can be evaluated in any order as long as the 885 * following conditions are met: 886 * 1) a closure must be evaluated before any other closures that take its 887 * futures as inputs; 888 * 2) all closures added before an invoke closure must be evaluated 889 * before it; 890 * and 3) all closures added after an invoke closure must be evaluated after 891 * it. 892 * As a special case, the order that the closures are added is a legal 893 * evaluation order. However, other evaluation orders are possible, including 894 * concurrently evaluating independent closures. 895 */ 896 897 public static final class Builder2 { 898 RenderScript mRS; 899 List<Closure> mClosures; 900 List<Input> mInputs; 901 private static final String TAG = "ScriptGroup.Builder2"; 902 903 /** 904 * Returns a Builder object 905 * 906 * @param rs the RenderScript context 907 */ 908 public Builder2(RenderScript rs) { 909 mRS = rs; 910 mClosures = new ArrayList<Closure>(); 911 mInputs = new ArrayList<Input>(); 912 } 913 914 /** 915 * Adds a closure for a kernel 916 * 917 * @param k Kernel ID for the kernel function 918 * @param returnType Allocation type for the return value 919 * @param args arguments to the kernel function 920 * @param globalBindings bindings for global variables 921 * @return a closure 922 */ 923 924 private Closure addKernelInternal(Script.KernelID k, Type returnType, Object[] args, 925 Map<Script.FieldID, Object> globalBindings) { 926 Closure c = new Closure(mRS, k, returnType, args, globalBindings); 927 mClosures.add(c); 928 return c; 929 } 930 931 /** 932 * Adds a closure for an invocable function 933 * 934 * @param invoke Invoke ID for the invocable function 935 * @param args arguments to the invocable function 936 * @param globalBindings bindings for global variables 937 * @return a closure 938 */ 939 940 private Closure addInvokeInternal(Script.InvokeID invoke, Object[] args, 941 Map<Script.FieldID, Object> globalBindings) { 942 Closure c = new Closure(mRS, invoke, args, globalBindings); 943 mClosures.add(c); 944 return c; 945 } 946 947 /** 948 * Adds a script group input 949 * 950 * @return a script group input, which can be used as an argument or a value to 951 * a global variable for creating closures 952 */ 953 public Input addInput() { 954 Input unbound = new Input(); 955 mInputs.add(unbound); 956 return unbound; 957 } 958 959 /** 960 * Adds a closure for a kernel 961 * 962 * @param k Kernel ID for the kernel function 963 * @param argsAndBindings arguments followed by bindings for global variables 964 * @return a closure 965 */ 966 967 public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) { 968 ArrayList<Object> args = new ArrayList<Object>(); 969 Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>(); 970 if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) { 971 return null; 972 } 973 return addKernelInternal(k, returnType, args.toArray(), bindingMap); 974 } 975 976 /** 977 * Adds a closure for an invocable function 978 * 979 * @param invoke Invoke ID for the invocable function 980 * @param argsAndBindings arguments followed by bindings for global variables 981 * @return a closure 982 */ 983 984 public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) { 985 ArrayList<Object> args = new ArrayList<Object>(); 986 Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>(); 987 if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) { 988 return null; 989 } 990 return addInvokeInternal(invoke, args.toArray(), bindingMap); 991 } 992 993 /** 994 * Creates a script group 995 * 996 * @param name name for the script group. Legal names can only contain letters, digits, 997 * '-', or '_'. The name can be no longer than 100 characters. 998 * Try to use unique names, to avoid name conflicts and reduce 999 * the cost of group creation. 1000 * @param outputs futures intended as outputs of the script group 1001 * @return a script group 1002 */ 1003 1004 public ScriptGroup create(String name, Future... outputs) { 1005 if (name == null || name.isEmpty() || name.length() > 100 || 1006 !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) { 1007 throw new RSIllegalArgumentException("invalid script group name"); 1008 } 1009 ScriptGroup ret = new ScriptGroup(mRS, name, mClosures, mInputs, outputs); 1010 return ret; 1011 } 1012 1013 private boolean seperateArgsAndBindings(Object[] argsAndBindings, 1014 ArrayList<Object> args, 1015 Map<Script.FieldID, Object> bindingMap) { 1016 int i; 1017 for (i = 0; i < argsAndBindings.length; i++) { 1018 if (argsAndBindings[i] instanceof Binding) { 1019 break; 1020 } 1021 args.add(argsAndBindings[i]); 1022 } 1023 1024 for (; i < argsAndBindings.length; i++) { 1025 if (!(argsAndBindings[i] instanceof Binding)) { 1026 return false; 1027 } 1028 Binding b = (Binding)argsAndBindings[i]; 1029 bindingMap.put(b.getField(), b.getValue()); 1030 } 1031 1032 return true; 1033 } 1034 1035 } 1036 1037} 1038