blob: 226d4831a66b17288e5156ef4f2d28450d594b74 [file] [log] [blame]
/*
* 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;
}
}