/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.renderscript; import java.lang.reflect.Method; /** * @hide **/ public class ScriptGroup extends BaseObj { Node mNodes[]; Connection mConnections[]; Node mFirstNode; IO mOutputs[]; IO mInputs[]; static class IO { Script mScript; Allocation mAllocation; String mName; IO(Script s) { mScript = s; } IO(Script s, String n) { mScript = s; mName = n; } } static class Connection { Node mTo[]; String mToName[]; Node mFrom; Type mAllocationType; Allocation mInternalAllocation; Connection(Node out, Type t) { mFrom = out; mAllocationType = t; } void addTo(Node n, String name) { if (mTo == null) { mTo = new Node[1]; mToName = new String[1]; } else { Node nt[] = new Node[mTo.length + 1]; String ns[] = new String[mTo.length + 1]; System.arraycopy(mTo, 0, nt, 0, mTo.length); System.arraycopy(mToName, 0, ns, 0, mTo.length); mTo = nt; mToName = ns; } mTo[mTo.length - 1] = n; mToName[mTo.length - 1] = name; } } static class Node { Script mScript; Connection mInput[] = new Connection[8]; Connection mOutput[] = new Connection[1]; int mInputCount; int mOutputCount; int mDepth; boolean mSeen; Node mNext; Node(Script s) { mScript = s; } void addInput(Connection c) { if (mInput.length <= mInputCount) { Connection[] nc = new Connection[mInput.length + 8]; System.arraycopy(mInput, 0, nc, 0, mInputCount); mInput = nc; } mInput[mInputCount++] = c; } void addOutput(Connection c) { if (mOutput.length <= mOutputCount) { Connection[] nc = new Connection[mOutput.length + 8]; System.arraycopy(mOutput, 0, nc, 0, mOutputCount); mOutput = nc; } mOutput[mOutputCount++] = c; } } ScriptGroup(int id, RenderScript rs) { super(id, rs); } void init(int nodeCount, int connectionCount) { mNodes = new Node[nodeCount]; mConnections = new Connection[connectionCount]; android.util.Log.v("RSR", "init" + nodeCount + ", " + connectionCount); // Count outputs and create array. Node n = mFirstNode; int outputCount = 0; int inputCount = 0; int connectionIndex = 0; int nodeNum = 0; while (n != null) { mNodes[nodeNum++] = n; // Look for unattached kernel inputs boolean hasInput = false; for (int ct=0; ct < n.mInput.length; ct++) { if (n.mInput[ct] != null) { if (n.mInput[ct].mToName == null) { hasInput = true; } } } if (!hasInput) { if (mInputs == null) { mInputs = new IO[1]; } if (mInputs.length <= inputCount) { IO t[] = new IO[mInputs.length + 1]; System.arraycopy(mInputs, 0, t, 0, mInputs.length); mInputs = t; } mInputs[inputCount++] = new IO(n.mScript); } // Look for unattached kernel outputs boolean hasOutput = false; for (int ct=0; ct < n.mOutput.length; ct++) { if (n.mOutput[ct] != null) { hasOutput = true; } } if (!hasOutput) { if (mOutputs == null) { mOutputs = new IO[1]; } if (mOutputs.length <= outputCount) { IO t[] = new IO[mOutputs.length + 1]; System.arraycopy(mOutputs, 0, t, 0, mOutputs.length); mOutputs = t; } mOutputs[outputCount++] = new IO(n.mScript); } // Make allocations for internal connections // Since script outputs are unique, use those to avoid duplicates. for (int ct=0; ct < n.mOutput.length; ct++) { android.util.Log.v("RSR", "init out2 " + n.mOutput[ct]); if (n.mOutput[ct] != null) { Connection t = n.mOutput[ct]; mConnections[connectionIndex++] = t; t.mInternalAllocation = Allocation.createTyped(mRS, t.mAllocationType); } } n = n.mNext; } } public void setInput(Script s, Allocation a) { for (int ct=0; ct < mInputs.length; ct++) { if (mInputs[ct].mScript == s) { mInputs[ct].mAllocation = a; return; } } throw new RSIllegalArgumentException("Script not found"); } public void setOutput(Script s, Allocation a) { for (int ct=0; ct < mOutputs.length; ct++) { if (mOutputs[ct].mScript == s) { mOutputs[ct].mAllocation = a; return; } } throw new RSIllegalArgumentException("Script not found"); } public void execute() { android.util.Log.v("RSR", "execute"); boolean more = true; int depth = 0; while (more) { more = false; for (int ct=0; ct < mNodes.length; ct++) { if (mNodes[ct].mDepth == depth) { more = true; Allocation kernelIn = null; for (int ct2=0; ct2 < mNodes[ct].mInputCount; ct2++) { android.util.Log.v("RSR", " kin " + ct2 + ", to " + mNodes[ct].mInput[ct2].mTo[0] + ", name " + mNodes[ct].mInput[ct2].mToName[0]); if (mNodes[ct].mInput[ct2].mToName[0] == null) { kernelIn = mNodes[ct].mInput[ct2].mInternalAllocation; break; } } Allocation kernelOut= null; for (int ct2=0; ct2 < mNodes[ct].mOutputCount; ct2++) { android.util.Log.v("RSR", " kout " + ct2 + ", from " + mNodes[ct].mOutput[ct2].mFrom); if (mNodes[ct].mOutput[ct2].mFrom != null) { kernelOut = mNodes[ct].mOutput[ct2].mInternalAllocation; break; } } if (kernelOut == null) { for (int ct2=0; ct2 < mOutputs.length; ct2++) { if (mOutputs[ct2].mScript == mNodes[ct].mScript) { kernelOut = mOutputs[ct2].mAllocation; break; } } } android.util.Log.v("RSR", "execute calling " + mNodes[ct] + ", with " + kernelIn); if (kernelIn != null) { try { Method m = mNodes[ct].mScript.getClass().getMethod("forEach_root", new Class[] { Allocation.class, Allocation.class }); m.invoke(mNodes[ct].mScript, new Object[] {kernelIn, kernelOut} ); } catch (Throwable t) { android.util.Log.e("RSR", "execute error " + t); } } else { try { Method m = mNodes[ct].mScript.getClass().getMethod("forEach_root", new Class[] { Allocation.class }); m.invoke(mNodes[ct].mScript, new Object[] {kernelOut} ); } catch (Throwable t) { android.util.Log.e("RSR", "execute error " + t); } } } } depth ++; } } public static class Builder { RenderScript mRS; Node mFirstNode; int mConnectionCount = 0; int mNodeCount = 0; public Builder(RenderScript rs) { mRS = rs; } private void validateRecurse(Node n, int depth) { n.mSeen = true; if (depth > n.mDepth) { n.mDepth = depth; } android.util.Log.v("RSR", " validateRecurse outputCount " + n.mOutputCount); for (int ct=0; ct < n.mOutputCount; ct++) { for (int ct2=0; ct2 < n.mOutput[ct].mTo.length; ct2++) { if (n.mOutput[ct].mTo[ct2].mSeen) { throw new RSInvalidStateException("Loops in group not allowed."); } validateRecurse(n.mOutput[ct].mTo[ct2], depth + 1); } } } private void validate() { android.util.Log.v("RSR", "validate"); Node n = mFirstNode; while (n != null) { n.mSeen = false; n.mDepth = 0; n = n.mNext; } n = mFirstNode; while (n != null) { android.util.Log.v("RSR", "validate n= " + n); if ((n.mSeen == false) && (n.mInputCount == 0)) { android.util.Log.v("RSR", " recursing " + n); validateRecurse(n, 0); } n = n.mNext; } } private Node findScript(Script s) { Node n = mFirstNode; while (n != null) { if (n.mScript == s) { return n; } n = n.mNext; } return null; } private void addNode(Node n) { n.mNext = mFirstNode; mFirstNode = n; } public Builder addConnection(Type t, Script output, Script input, String inputName) { android.util.Log.v("RSR", "addConnection " + t +", " + output + ", " + input); // Look for existing output Node nout = findScript(output); Connection c; if (nout == null) { // Make new node android.util.Log.v("RSR", "addConnection new output node"); nout = new Node(output); mNodeCount++; c = new Connection(nout, t); mConnectionCount++; nout.addOutput(c); addNode(nout); } else { // Add to existing node android.util.Log.v("RSR", "addConnection reuse output node"); if (nout.mOutput[0] != null) { if (nout.mOutput[0].mFrom.mScript != output) { throw new RSInvalidStateException("Changed output of existing node"); } if (nout.mOutput[0].mAllocationType != t) { throw new RSInvalidStateException("Changed output type of existing node"); } } c = nout.mOutput[0]; } // At this point we should have a connection attached to a script ouput. // Find input Node nin = findScript(input); if (nin == null) { android.util.Log.v("RSR", "addConnection new input node"); nin = new Node(input); mNodeCount++; addNode(nin); } c.addTo(nin, inputName); nin.addInput(c); validate(); return this; } public ScriptGroup create() { ScriptGroup sg = new ScriptGroup(0, mRS); sg.mFirstNode = mFirstNode; mFirstNode = null; android.util.Log.v("RSR", "create nodes= " + mNodeCount + ", Connections= " + mConnectionCount); sg.init(mNodeCount, mConnectionCount); return sg; } } }