• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.ide.eclipse.gltrace.editors;
18 
19 import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.Function;
20 import com.android.ide.eclipse.gltrace.model.GLCall;
21 import com.android.ide.eclipse.gltrace.model.GLTrace;
22 
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Stack;
26 
27 public class GLCallGroups {
28     /**
29      * A {@link GLCallNode} is a simple wrapper around a {@link GLCall} that
30      * adds the notion of hierarchy.
31      */
32     public interface GLCallNode {
33         /** Does this call have child nodes? */
hasChildren()34         boolean hasChildren();
35 
36         /** Returns a list of child nodes of this call. */
getChildren()37         List<GLCallNode> getChildren();
38 
39         /** Returns the {@link GLCall} that is wrapped by this node. */
getCall()40         GLCall getCall();
41 
42         /** Returns the parent of this node, the parent is null if this is a top level node */
getParent()43         GLCallNode getParent();
44 
45         /** Set the parent node. */
setParent(GLCallNode parent)46         void setParent(GLCallNode parent);
47     }
48 
49     private static class GLTreeNode implements GLCallNode {
50         private final GLCall mCall;
51         private GLCallNode mParent;
52         private List<GLCallNode> mGLCallNodes;
53 
GLTreeNode(GLCall call)54         public GLTreeNode(GLCall call) {
55             mCall = call;
56             mGLCallNodes = new ArrayList<GLCallNode>();
57         }
58 
59         @Override
hasChildren()60         public boolean hasChildren() {
61             return true;
62         }
63 
64         @Override
getParent()65         public GLCallNode getParent() {
66             return mParent;
67         }
68 
69         @Override
setParent(GLCallNode parent)70         public void setParent(GLCallNode parent) {
71             mParent = parent;
72         }
73 
74         @Override
getChildren()75         public List<GLCallNode> getChildren() {
76             return mGLCallNodes;
77         }
78 
addChild(GLCallNode n)79         public void addChild(GLCallNode n) {
80             mGLCallNodes.add(n);
81             n.setParent(this);
82         }
83 
84         @Override
getCall()85         public GLCall getCall() {
86             return mCall;
87         }
88     }
89 
90     private static class GLLeafNode implements GLCallNode {
91         private final GLCall mCall;
92         private GLCallNode mParent;
93 
GLLeafNode(GLCall call)94         public GLLeafNode(GLCall call) {
95             mCall = call;
96         }
97 
98         @Override
hasChildren()99         public boolean hasChildren() {
100             return false;
101         }
102 
103         @Override
getChildren()104         public List<GLCallNode> getChildren() {
105             return null;
106         }
107 
108         @Override
getParent()109         public GLCallNode getParent() {
110             return mParent;
111         }
112 
113         @Override
setParent(GLCallNode parent)114         public void setParent(GLCallNode parent) {
115             mParent = parent;
116         }
117 
118         @Override
getCall()119         public GLCall getCall() {
120             return mCall;
121         }
122     }
123 
124     /**
125      * Impose a hierarchy on a list of {@link GLCall}'s based on the presence of
126      * {@link Function#glPushGroupMarkerEXT} and {@link Function#glPopGroupMarkerEXT} calls.
127      * Such a hierarchy is possible only if calls from a single context are considered.
128      * @param trace trace to look at
129      * @param start starting call index
130      * @param end ending call index
131      * @param contextToGroup context from which calls should be grouped. If no such context
132      *        is present, then all calls in the given range will be returned back as a flat
133      *        list.
134      * @return a tree structured list of {@link GLCallNode} objects
135      */
constructCallHierarchy(GLTrace trace, int start, int end, int contextToGroup)136     public static List<GLCallNode> constructCallHierarchy(GLTrace trace, int start, int end,
137             int contextToGroup) {
138         if (contextToGroup < 0 || contextToGroup > trace.getContexts().size()) {
139             return flatHierarchy(trace, start, end);
140         }
141 
142         List<GLCall> calls = trace.getGLCalls();
143 
144         Stack<GLTreeNode> hierarchyStack = new Stack<GLTreeNode>();
145         List<GLCallNode> items = new ArrayList<GLCallNode>();
146 
147         for (int i = start; i < end; i++) {
148             GLCall c = calls.get(i);
149             if (c.getContextId() != contextToGroup) {
150                 // skip this call if it is not part of the context we need to display
151                 continue;
152             }
153 
154             if (c.getFunction() == Function.glPushGroupMarkerEXT) {
155                 GLTreeNode group = new GLTreeNode(c);
156                 if (hierarchyStack.size() > 0) {
157                     hierarchyStack.peek().addChild(group);
158                 } else {
159                     items.add(group);
160                 }
161                 hierarchyStack.push(group);
162             } else if (c.getFunction() == Function.glPopGroupMarkerEXT) {
163                 if (hierarchyStack.size() > 0) {
164                     hierarchyStack.pop();
165                 } else {
166                     // FIXME: If we are attempting to pop from an empty stack,
167                     // that implies that a push marker was seen in a prior frame
168                     // (in a call before @start). In such a case, we simply continue
169                     // adding further calls to the root of the hierarchy rather than
170                     // searching backwards in the call list for the corresponding
171                     // push markers.
172                     items.add(new GLLeafNode(c));
173                 }
174             } else {
175               GLLeafNode leaf = new GLLeafNode(c);
176               if (hierarchyStack.size() > 0) {
177                   hierarchyStack.peek().addChild(leaf);
178               } else {
179                   items.add(leaf);
180               }
181             }
182         }
183 
184         return items;
185     }
186 
flatHierarchy(GLTrace trace, int start, int end)187     private static List<GLCallNode> flatHierarchy(GLTrace trace, int start, int end) {
188         List<GLCallNode> items = new ArrayList<GLCallNode>();
189 
190         List<GLCall> calls = trace.getGLCalls();
191         for (int i = start; i < end; i++) {
192             items.add(new GLLeafNode(calls.get(i)));
193         }
194 
195         return items;
196     }
197 }
198