/* * 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 com.android.ide.eclipse.gltrace.editors; import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.Function; import com.android.ide.eclipse.gltrace.model.GLCall; import com.android.ide.eclipse.gltrace.model.GLTrace; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Stack; public class GLCallGroups { /** * A {@link GLCallNode} is a simple wrapper around a {@link GLCall} that * adds the notion of hierarchy. */ public interface GLCallNode { /** Does this call have child nodes? */ boolean hasChildren(); /** Returns a list of child nodes of this call. */ List<GLCallNode> getChildren(); /** Returns the {@link GLCall} that is wrapped by this node. */ GLCall getCall(); /** Returns the parent of this node, the parent is null if this is a top level node */ GLCallNode getParent(); /** Set the parent node. */ void setParent(GLCallNode parent); } private static class GLTreeNode implements GLCallNode { private final GLCall mCall; private GLCallNode mParent; private List<GLCallNode> mGLCallNodes; public GLTreeNode(GLCall call) { mCall = call; mGLCallNodes = new ArrayList<GLCallNode>(); } @Override public boolean hasChildren() { return true; } @Override public GLCallNode getParent() { return mParent; } @Override public void setParent(GLCallNode parent) { mParent = parent; } @Override public List<GLCallNode> getChildren() { return mGLCallNodes; } public void addChild(GLCallNode n) { mGLCallNodes.add(n); n.setParent(this); } @Override public GLCall getCall() { return mCall; } } private static class GLLeafNode implements GLCallNode { private final GLCall mCall; private GLCallNode mParent; public GLLeafNode(GLCall call) { mCall = call; } @Override public boolean hasChildren() { return false; } @Override public List<GLCallNode> getChildren() { return null; } @Override public GLCallNode getParent() { return mParent; } @Override public void setParent(GLCallNode parent) { mParent = parent; } @Override public GLCall getCall() { return mCall; } } /** * Impose a hierarchy on a list of {@link GLCall}'s based on the presence of * {@link Function#glPushGroupMarkerEXT} and {@link Function#glPopGroupMarkerEXT} calls. * Such a hierarchy is possible only if calls from a single context are considered. * @param trace trace to look at * @param start starting call index * @param end ending call index * @param contextToGroup context from which calls should be grouped. If no such context * is present, then all calls in the given range will be returned back as a flat * list. * @return a tree structured list of {@link GLCallNode} objects */ public static List<GLCallNode> constructCallHierarchy(GLTrace trace, int start, int end, int contextToGroup) { if (trace == null) { return Collections.emptyList(); } if (contextToGroup < 0 || contextToGroup > trace.getContexts().size()) { return flatHierarchy(trace, start, end); } List<GLCall> calls = trace.getGLCalls(); Stack<GLTreeNode> hierarchyStack = new Stack<GLTreeNode>(); List<GLCallNode> items = new ArrayList<GLCallNode>(); for (int i = start; i < end; i++) { GLCall c = calls.get(i); if (c.getContextId() != contextToGroup) { // skip this call if it is not part of the context we need to display continue; } if (c.getFunction() == Function.glPushGroupMarkerEXT) { GLTreeNode group = new GLTreeNode(c); if (hierarchyStack.size() > 0) { hierarchyStack.peek().addChild(group); } else { items.add(group); } hierarchyStack.push(group); } else if (c.getFunction() == Function.glPopGroupMarkerEXT) { if (hierarchyStack.size() > 0) { hierarchyStack.pop(); } else { // FIXME: If we are attempting to pop from an empty stack, // that implies that a push marker was seen in a prior frame // (in a call before @start). In such a case, we simply continue // adding further calls to the root of the hierarchy rather than // searching backwards in the call list for the corresponding // push markers. items.add(new GLLeafNode(c)); } } else { GLLeafNode leaf = new GLLeafNode(c); if (hierarchyStack.size() > 0) { hierarchyStack.peek().addChild(leaf); } else { items.add(leaf); } } } return items; } private static List<GLCallNode> flatHierarchy(GLTrace trace, int start, int end) { List<GLCallNode> items = new ArrayList<GLCallNode>(); List<GLCall> calls = trace.getGLCalls(); for (int i = start; i < end; i++) { items.add(new GLLeafNode(calls.get(i))); } return items; } }