Home | History | Annotate | Download | only in editors
      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? */
     35         boolean hasChildren();
     36 
     37         /** Returns a list of child nodes of this call. */
     38         List<GLCallNode> getChildren();
     39 
     40         /** Returns the {@link GLCall} that is wrapped by this node. */
     41         GLCall getCall();
     42 
     43         /** Returns the parent of this node, the parent is null if this is a top level node */
     44         GLCallNode getParent();
     45 
     46         /** Set the parent node. */
     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 
     55         public GLTreeNode(GLCall call) {
     56             mCall = call;
     57             mGLCallNodes = new ArrayList<GLCallNode>();
     58         }
     59 
     60         @Override
     61         public boolean hasChildren() {
     62             return true;
     63         }
     64 
     65         @Override
     66         public GLCallNode getParent() {
     67             return mParent;
     68         }
     69 
     70         @Override
     71         public void setParent(GLCallNode parent) {
     72             mParent = parent;
     73         }
     74 
     75         @Override
     76         public List<GLCallNode> getChildren() {
     77             return mGLCallNodes;
     78         }
     79 
     80         public void addChild(GLCallNode n) {
     81             mGLCallNodes.add(n);
     82             n.setParent(this);
     83         }
     84 
     85         @Override
     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 
     95         public GLLeafNode(GLCall call) {
     96             mCall = call;
     97         }
     98 
     99         @Override
    100         public boolean hasChildren() {
    101             return false;
    102         }
    103 
    104         @Override
    105         public List<GLCallNode> getChildren() {
    106             return null;
    107         }
    108 
    109         @Override
    110         public GLCallNode getParent() {
    111             return mParent;
    112         }
    113 
    114         @Override
    115         public void setParent(GLCallNode parent) {
    116             mParent = parent;
    117         }
    118 
    119         @Override
    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      */
    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 
    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