ScriptGroup.java revision 423ebcb4dc4881c3a83e8121d5212466287d0d0c
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 java.lang.reflect.Method;
20
21/**
22 * @hide
23 **/
24public class ScriptGroup extends BaseObj {
25    Node mNodes[];
26    Connection mConnections[];
27    Node mFirstNode;
28    IO mOutputs[];
29    IO mInputs[];
30
31    static class IO {
32        Script mScript;
33        Allocation mAllocation;
34        String mName;
35
36        IO(Script s) {
37            mScript = s;
38        }
39        IO(Script s, String n) {
40            mScript = s;
41            mName = n;
42        }
43    }
44
45    static class Connection {
46        Node mTo[];
47        String mToName[];
48        Node mFrom;
49        Type mAllocationType;
50        Allocation mInternalAllocation;
51
52        Connection(Node out, Type t) {
53            mFrom = out;
54            mAllocationType = t;
55        }
56
57        void addTo(Node n, String name) {
58            if (mTo == null) {
59                mTo = new Node[1];
60                mToName = new String[1];
61            } else {
62                Node nt[] = new Node[mTo.length + 1];
63                String ns[] = new String[mTo.length + 1];
64                System.arraycopy(mTo, 0, nt, 0, mTo.length);
65                System.arraycopy(mToName, 0, ns, 0, mTo.length);
66                mTo = nt;
67                mToName = ns;
68            }
69            mTo[mTo.length - 1] = n;
70            mToName[mTo.length - 1] = name;
71        }
72    }
73
74    static class Node {
75        Script mScript;
76        Connection mInput[] = new Connection[8];
77        Connection mOutput[] = new Connection[1];
78        int mInputCount;
79        int mOutputCount;
80        int mDepth;
81        boolean mSeen;
82
83        Node mNext;
84
85        Node(Script s) {
86            mScript = s;
87        }
88
89        void addInput(Connection c) {
90            if (mInput.length <= mInputCount) {
91                Connection[] nc = new Connection[mInput.length + 8];
92                System.arraycopy(mInput, 0, nc, 0, mInputCount);
93                mInput = nc;
94            }
95            mInput[mInputCount++] = c;
96        }
97
98        void addOutput(Connection c) {
99            if (mOutput.length <= mOutputCount) {
100                Connection[] nc = new Connection[mOutput.length + 8];
101                System.arraycopy(mOutput, 0, nc, 0, mOutputCount);
102                mOutput = nc;
103            }
104            mOutput[mOutputCount++] = c;
105        }
106    }
107
108
109    ScriptGroup(int id, RenderScript rs) {
110        super(id, rs);
111    }
112
113    void init(int nodeCount, int connectionCount) {
114        mNodes = new Node[nodeCount];
115        mConnections = new Connection[connectionCount];
116
117        android.util.Log.v("RSR", "init" + nodeCount + ", " + connectionCount);
118
119        // Count outputs and create array.
120        Node n = mFirstNode;
121        int outputCount = 0;
122        int inputCount = 0;
123        int connectionIndex = 0;
124        int nodeNum = 0;
125        while (n != null) {
126            mNodes[nodeNum++] = n;
127
128            // Look for unattached kernel inputs
129            boolean hasInput = false;
130            for (int ct=0; ct < n.mInput.length; ct++) {
131                if (n.mInput[ct] != null) {
132                    if (n.mInput[ct].mToName == null) {
133                        hasInput = true;
134                    }
135                }
136            }
137            if (!hasInput) {
138                if (mInputs == null) {
139                    mInputs = new IO[1];
140                }
141                if (mInputs.length <= inputCount) {
142                    IO t[] = new IO[mInputs.length + 1];
143                    System.arraycopy(mInputs, 0, t, 0, mInputs.length);
144                    mInputs = t;
145                }
146                mInputs[inputCount++] = new IO(n.mScript);
147            }
148
149            // Look for unattached kernel outputs
150            boolean hasOutput = false;
151            for (int ct=0; ct < n.mOutput.length; ct++) {
152                if (n.mOutput[ct] != null) {
153                    hasOutput = true;
154                }
155            }
156            if (!hasOutput) {
157                if (mOutputs == null) {
158                    mOutputs = new IO[1];
159                }
160                if (mOutputs.length <= outputCount) {
161                    IO t[] = new IO[mOutputs.length + 1];
162                    System.arraycopy(mOutputs, 0, t, 0, mOutputs.length);
163                    mOutputs = t;
164                }
165                mOutputs[outputCount++] = new IO(n.mScript);
166            }
167
168            // Make allocations for internal connections
169            // Since script outputs are unique, use those to avoid duplicates.
170            for (int ct=0; ct < n.mOutput.length; ct++) {
171                android.util.Log.v("RSR", "init out2 " + n.mOutput[ct]);
172                if (n.mOutput[ct] != null) {
173                    Connection t = n.mOutput[ct];
174                    mConnections[connectionIndex++] = t;
175                    t.mInternalAllocation = Allocation.createTyped(mRS, t.mAllocationType);
176                }
177            }
178
179            n = n.mNext;
180        }
181    }
182
183    public void setInput(Script s, Allocation a) {
184        for (int ct=0; ct < mInputs.length; ct++) {
185            if (mInputs[ct].mScript == s) {
186                mInputs[ct].mAllocation = a;
187                return;
188            }
189        }
190        throw new RSIllegalArgumentException("Script not found");
191    }
192
193    public void setOutput(Script s, Allocation a) {
194        for (int ct=0; ct < mOutputs.length; ct++) {
195            if (mOutputs[ct].mScript == s) {
196                mOutputs[ct].mAllocation = a;
197                return;
198            }
199        }
200        throw new RSIllegalArgumentException("Script not found");
201    }
202
203    public void execute() {
204        android.util.Log.v("RSR", "execute");
205        boolean more = true;
206        int depth = 0;
207        while (more) {
208            more = false;
209            for (int ct=0; ct < mNodes.length; ct++) {
210                if (mNodes[ct].mDepth == depth) {
211                    more = true;
212
213                    Allocation kernelIn = null;
214                    for (int ct2=0; ct2 < mNodes[ct].mInputCount; ct2++) {
215                        android.util.Log.v("RSR", " kin " + ct2 + ", to " + mNodes[ct].mInput[ct2].mTo[0] + ", name " + mNodes[ct].mInput[ct2].mToName[0]);
216                        if (mNodes[ct].mInput[ct2].mToName[0] == null) {
217                            kernelIn = mNodes[ct].mInput[ct2].mInternalAllocation;
218                            break;
219                        }
220                    }
221
222                    Allocation kernelOut= null;
223                    for (int ct2=0; ct2 < mNodes[ct].mOutputCount; ct2++) {
224                        android.util.Log.v("RSR", " kout " + ct2 + ", from " + mNodes[ct].mOutput[ct2].mFrom);
225                        if (mNodes[ct].mOutput[ct2].mFrom != null) {
226                            kernelOut = mNodes[ct].mOutput[ct2].mInternalAllocation;
227                            break;
228                        }
229                    }
230                    if (kernelOut == null) {
231                        for (int ct2=0; ct2 < mOutputs.length; ct2++) {
232                            if (mOutputs[ct2].mScript == mNodes[ct].mScript) {
233                                kernelOut = mOutputs[ct2].mAllocation;
234                                break;
235                            }
236                        }
237                    }
238
239                    android.util.Log.v("RSR", "execute calling " + mNodes[ct] + ", with " + kernelIn);
240                    if (kernelIn != null) {
241                        try {
242
243                            Method m = mNodes[ct].mScript.getClass().getMethod("forEach_root",
244                                          new Class[] { Allocation.class, Allocation.class });
245                            m.invoke(mNodes[ct].mScript, new Object[] {kernelIn, kernelOut} );
246                        } catch (Throwable t) {
247                            android.util.Log.e("RSR", "execute error " + t);
248                        }
249                    } else {
250                        try {
251                            Method m = mNodes[ct].mScript.getClass().getMethod("forEach_root",
252                                          new Class[] { Allocation.class });
253                            m.invoke(mNodes[ct].mScript, new Object[] {kernelOut} );
254                        } catch (Throwable t) {
255                            android.util.Log.e("RSR", "execute error " + t);
256                        }
257                    }
258
259                }
260            }
261            depth ++;
262        }
263
264    }
265
266
267    public static class Builder {
268        RenderScript mRS;
269        Node mFirstNode;
270        int mConnectionCount = 0;
271        int mNodeCount = 0;
272
273        public Builder(RenderScript rs) {
274            mRS = rs;
275        }
276
277        private void validateRecurse(Node n, int depth) {
278            n.mSeen = true;
279            if (depth > n.mDepth) {
280                n.mDepth = depth;
281            }
282
283            android.util.Log.v("RSR", " validateRecurse outputCount " + n.mOutputCount);
284            for (int ct=0; ct < n.mOutputCount; ct++) {
285                for (int ct2=0; ct2 < n.mOutput[ct].mTo.length; ct2++) {
286                    if (n.mOutput[ct].mTo[ct2].mSeen) {
287                        throw new RSInvalidStateException("Loops in group not allowed.");
288                    }
289                    validateRecurse(n.mOutput[ct].mTo[ct2], depth + 1);
290                }
291            }
292        }
293
294        private void validate() {
295            android.util.Log.v("RSR", "validate");
296            Node n = mFirstNode;
297            while (n != null) {
298                n.mSeen = false;
299                n.mDepth = 0;
300                n = n.mNext;
301            }
302
303            n = mFirstNode;
304            while (n != null) {
305                android.util.Log.v("RSR", "validate n= " + n);
306                if ((n.mSeen == false) && (n.mInputCount == 0)) {
307                    android.util.Log.v("RSR", " recursing " + n);
308                    validateRecurse(n, 0);
309                }
310                n = n.mNext;
311            }
312        }
313
314        private Node findScript(Script s) {
315            Node n = mFirstNode;
316            while (n != null) {
317                if (n.mScript == s) {
318                    return n;
319                }
320                n = n.mNext;
321            }
322            return null;
323        }
324
325        private void addNode(Node n) {
326            n.mNext = mFirstNode;
327            mFirstNode = n;
328        }
329
330        public Builder addConnection(Type t, Script output, Script input, String inputName) {
331            android.util.Log.v("RSR", "addConnection " + t +", " + output + ", " + input);
332
333            // Look for existing output
334            Node nout = findScript(output);
335            Connection c;
336            if (nout == null) {
337                // Make new node
338                android.util.Log.v("RSR", "addConnection new output node");
339                nout = new Node(output);
340                mNodeCount++;
341                c = new Connection(nout, t);
342                mConnectionCount++;
343                nout.addOutput(c);
344                addNode(nout);
345            } else {
346                // Add to existing node
347                android.util.Log.v("RSR", "addConnection reuse output node");
348                if (nout.mOutput[0] != null) {
349                    if (nout.mOutput[0].mFrom.mScript != output) {
350                        throw new RSInvalidStateException("Changed output of existing node");
351                    }
352                    if (nout.mOutput[0].mAllocationType != t) {
353                        throw new RSInvalidStateException("Changed output type of existing node");
354                    }
355                }
356                c = nout.mOutput[0];
357            }
358            // At this point we should have a connection attached to a script ouput.
359
360            // Find input
361            Node nin = findScript(input);
362            if (nin == null) {
363                android.util.Log.v("RSR", "addConnection new input node");
364                nin = new Node(input);
365                mNodeCount++;
366                addNode(nin);
367            }
368            c.addTo(nin, inputName);
369            nin.addInput(c);
370
371            validate();
372            return this;
373        }
374
375        public ScriptGroup create() {
376            ScriptGroup sg = new ScriptGroup(0, mRS);
377            sg.mFirstNode = mFirstNode;
378            mFirstNode = null;
379
380            android.util.Log.v("RSR", "create nodes= " + mNodeCount + ", Connections= " + mConnectionCount);
381
382            sg.init(mNodeCount, mConnectionCount);
383            return sg;
384        }
385
386    }
387
388
389}
390
391
392