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