aboutsummaryrefslogtreecommitdiffstats
path: root/eclipse/plugins/com.android.ide.eclipse.gldebugger
diff options
context:
space:
mode:
authorSiva Velusamy <vsiva@google.com>2012-02-21 14:45:54 -0800
committerSiva Velusamy <vsiva@google.com>2012-02-21 14:56:22 -0800
commit3824d578d3d56f7a4d65572fb942a1e035121cc8 (patch)
treef60bbf1107f5c933f695562424c9e8c72fa6d550 /eclipse/plugins/com.android.ide.eclipse.gldebugger
parent34e4ce9cb42f4db512103b47445d4145c09fc806 (diff)
downloadsdk-3824d578d3d56f7a4d65572fb942a1e035121cc8.zip
sdk-3824d578d3d56f7a4d65572fb942a1e035121cc8.tar.gz
sdk-3824d578d3d56f7a4d65572fb942a1e035121cc8.tar.bz2
gltrace: Show per frame summary statistics.
This CL changes the framebuffer view to be a frame summary view. The Frame Summary View displays summary information regarding the currently displayed frame. For each frame, it displays: - the contents of the framebuffer at the end of the frame. - summary statistics regarding the GL Calls present in the frame. Change-Id: I293f5b0de40aac315dee257fbc3eaa3d5ff0919c
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.gldebugger')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java18
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferViewPage.java119
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameSummaryView.java (renamed from eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferView.java)11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameSummaryViewPage.java401
6 files changed, 419 insertions, 138 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml
index d693916..66f7fa6 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/plugin.xml
@@ -17,10 +17,10 @@
</view>
<view
category="com.android.ide.eclipse.gltrace"
- class="com.android.ide.eclipse.gltrace.views.FrameBufferView"
+ class="com.android.ide.eclipse.gltrace.views.FrameSummaryView"
icon="icons/opengl.png"
id="com.android.ide.eclipse.gltrace.views.FrameBuffer"
- name="Framebuffer"
+ name="Frame Summary"
restorable="true">
</view>
<view
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java
index d8f1a0b..bc41e33 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTracePerspective.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.gltrace;
import com.android.ide.eclipse.gltrace.views.DetailsView;
-import com.android.ide.eclipse.gltrace.views.FrameBufferView;
+import com.android.ide.eclipse.gltrace.views.FrameSummaryView;
import com.android.ide.eclipse.gltrace.views.StateView;
import org.eclipse.ui.IFolderLayout;
@@ -51,6 +51,6 @@ public class GLTracePerspective implements IPerspectiveFactory {
IPageLayout.BOTTOM,
0.5f,
FB_FOLDER_ID);
- column3bottom.addView(FrameBufferView.ID);
+ column3bottom.addView(FrameSummaryView.ID);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
index 1a1f388..ece4723 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
@@ -25,7 +25,7 @@ import com.android.ide.eclipse.gltrace.model.GLCall;
import com.android.ide.eclipse.gltrace.model.GLFrame;
import com.android.ide.eclipse.gltrace.model.GLTrace;
import com.android.ide.eclipse.gltrace.views.DetailsPage;
-import com.android.ide.eclipse.gltrace.views.FrameBufferViewPage;
+import com.android.ide.eclipse.gltrace.views.FrameSummaryViewPage;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
@@ -112,7 +112,7 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
private boolean mShowContextSwitcher;
private int mCurrentlyDisplayedContext = -1;
- private FrameBufferViewPage mFrameBufferViewPage;
+ private FrameSummaryViewPage mFrameSummaryViewPage;
public GLFunctionTraceViewer() {
mGldrawTextColor = Display.getDefault().getSystemColor(SWT.COLOR_BLUE);
@@ -276,9 +276,9 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
// update minimap view
mDurationMinimap.setCallRangeForCurrentFrame(mCallStartIndex, mCallEndIndex);
- // update FB view
- if (mFrameBufferViewPage != null) {
- mFrameBufferViewPage.setSelectedFrame(selectedFrame - 1);
+ // update the frame summary view
+ if (mFrameSummaryViewPage != null) {
+ mFrameSummaryViewPage.setSelectedFrame(selectedFrame - 1);
}
}
@@ -656,12 +656,12 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
return new StateViewPage(mTrace);
}
- public FrameBufferViewPage getFrameBufferViewPage() {
- if (mFrameBufferViewPage == null) {
- mFrameBufferViewPage = new FrameBufferViewPage(mTrace);
+ public FrameSummaryViewPage getFrameSummaryViewPage() {
+ if (mFrameSummaryViewPage == null) {
+ mFrameSummaryViewPage = new FrameSummaryViewPage(mTrace);
}
- return mFrameBufferViewPage;
+ return mFrameSummaryViewPage;
}
public DetailsPage getDetailsPage() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferViewPage.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferViewPage.java
deleted file mode 100644
index 09b43b2..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferViewPage.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.views;
-
-import com.android.ide.eclipse.gltrace.editors.GLFunctionTraceViewer;
-import com.android.ide.eclipse.gltrace.editors.GLCallGroups.GLCallNode;
-import com.android.ide.eclipse.gltrace.model.GLCall;
-import com.android.ide.eclipse.gltrace.model.GLTrace;
-import com.android.ide.eclipse.gltrace.widgets.ImageCanvas;
-
-import org.eclipse.jface.action.IToolBarManager;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.TreeSelection;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.ISelectionListener;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.part.IPageSite;
-import org.eclipse.ui.part.Page;
-
-public class FrameBufferViewPage extends Page implements ISelectionListener {
- private ImageCanvas mImageCanvas;
-
- private final GLTrace mTrace;
-
- public FrameBufferViewPage(GLTrace trace) {
- mTrace = trace;
- }
-
- @Override
- public void createControl(Composite parent) {
- mImageCanvas = new ImageCanvas(parent);
-
- IToolBarManager toolbarManager = getSite().getActionBars().getToolBarManager();
- toolbarManager.add(new FitToCanvasAction(true, mImageCanvas));
- }
-
- @Override
- public Control getControl() {
- return mImageCanvas;
- }
-
- @Override
- public void init(IPageSite pageSite) {
- super.init(pageSite);
- pageSite.getPage().addSelectionListener(this);
- }
-
- @Override
- public void dispose() {
- getSite().getPage().removeSelectionListener(this);
- super.dispose();
- }
-
- @Override
- public void setFocus() {
- }
-
- @Override
- public void selectionChanged(IWorkbenchPart part, ISelection selection) {
- if (!(part instanceof GLFunctionTraceViewer)) {
- return;
- }
-
- if (((GLFunctionTraceViewer) part).getTrace() != mTrace) {
- return;
- }
-
- if (!(selection instanceof TreeSelection)) {
- return;
- }
-
- GLCall selectedCall = null;
-
- Object data = ((TreeSelection) selection).getFirstElement();
- if (data instanceof GLCallNode) {
- selectedCall = ((GLCallNode) data).getCall();
- }
-
- if (selectedCall == null) {
- return;
- }
-
- setImage(mTrace.getImage(selectedCall));
- }
-
- private void setImage(final Image image) {
- Display.getDefault().asyncExec(new Runnable() {
- @Override
- public void run() {
- mImageCanvas.setImage(image);
- }
- });
- };
-
- public void setSelectedFrame(int frame) {
- int lastCallIndex = mTrace.getFrame(frame).getEndIndex() - 1;
- if (lastCallIndex >= 0 && lastCallIndex < mTrace.getGLCalls().size()) {
- GLCall call = mTrace.getGLCalls().get(lastCallIndex);
- setImage(mTrace.getImage(call));
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferView.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameSummaryView.java
index 50eafd2..42eb98c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameBufferView.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameSummaryView.java
@@ -21,13 +21,12 @@ import com.android.ide.eclipse.gltrace.editors.GLFunctionTraceViewer;
import org.eclipse.ui.IWorkbenchPart;
/**
- * The {@link FrameBufferView} displays the contents of the frame buffer for the
- * currently displayed frame.
+ * The {@link FrameSummaryView} is a page book view with pages of type {@link FrameSummaryViewPage}.
*/
-public class FrameBufferView extends GLPageBookView {
+public class FrameSummaryView extends GLPageBookView {
public static final String ID = "com.android.ide.eclipse.gltrace.views.FrameBuffer"; //$NON-NLS-1$
- public FrameBufferView() {
+ public FrameSummaryView() {
super("Open a GL Trace file to view the framebuffer contents.");
}
@@ -38,7 +37,7 @@ public class FrameBufferView extends GLPageBookView {
}
GLFunctionTraceViewer viewer = (GLFunctionTraceViewer) part;
- FrameBufferViewPage page = viewer.getFrameBufferViewPage();
+ FrameSummaryViewPage page = viewer.getFrameSummaryViewPage();
initPage(page);
page.createControl(getPageBook());
@@ -47,7 +46,7 @@ public class FrameBufferView extends GLPageBookView {
@Override
protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
- FrameBufferViewPage page = (FrameBufferViewPage) pageRecord.page;
+ FrameSummaryViewPage page = (FrameSummaryViewPage) pageRecord.page;
page.dispose();
pageRecord.dispose();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameSummaryViewPage.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameSummaryViewPage.java
new file mode 100644
index 0000000..fbe131a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/FrameSummaryViewPage.java
@@ -0,0 +1,401 @@
+/*
+ * 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.views;
+
+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 com.android.ide.eclipse.gltrace.widgets.ImageCanvas;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.part.Page;
+
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A {@link FrameSummaryViewPage} displays summary information regarding a frame. This includes
+ * the contents of the frame buffer at the end of the frame, and statistics regarding the
+ * OpenGL Calls present in the frame.
+ */
+public class FrameSummaryViewPage extends Page {
+ private final GLTrace mTrace;
+
+ private int mCurrentFrame;
+
+ private SashForm mSash;
+ private ImageCanvas mImageCanvas;
+
+ private Label mWallClockTimeLabel;
+ private Label mThreadTimeLabel;
+
+ private TableViewer mStatsTableViewer;
+ private StatsLabelProvider mStatsLabelProvider;
+ private StatsTableComparator mStatsTableComparator;
+
+ private static final String[] STATS_TABLE_PROPERTIES = {
+ "Function",
+ "Count",
+ "Wall Time (ns)",
+ "Thread Time (ns)",
+ };
+ private static final float[] STATS_TABLE_COLWIDTH_RATIOS = {
+ 0.4f, 0.1f, 0.25f, 0.25f,
+ };
+ private static final int[] STATS_TABLE_COL_ALIGNMENT = {
+ SWT.LEFT, SWT.LEFT, SWT.RIGHT, SWT.RIGHT,
+ };
+
+ public FrameSummaryViewPage(GLTrace trace) {
+ mTrace = trace;
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ mSash = new SashForm(parent, SWT.VERTICAL);
+
+ // create image canvas where the framebuffer is displayed
+ mImageCanvas = new ImageCanvas(mSash);
+
+ // create a composite where the frame statistics are displayed
+ createFrameStatisticsPart(mSash);
+
+ mSash.setWeights(new int[] {70, 30});
+
+ IToolBarManager toolbarManager = getSite().getActionBars().getToolBarManager();
+ toolbarManager.add(new FitToCanvasAction(true, mImageCanvas));
+ }
+
+ private void createFrameStatisticsPart(Composite parent) {
+ Composite c = new Composite(parent, SWT.NONE);
+ c.setLayout(new GridLayout(2, false));
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(c);
+
+ Label l = new Label(c, SWT.NONE);
+ l.setText("Cumulative call duration of all OpenGL Calls in this frame:");
+ l.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(l);
+
+ l = new Label(c, SWT.NONE);
+ l.setText("Wall Clock Time: ");
+ GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.CENTER).applyTo(l);
+
+ mWallClockTimeLabel = new Label(c, SWT.NONE);
+ GridDataFactory.defaultsFor(mWallClockTimeLabel)
+ .grab(true, false)
+ .applyTo(mWallClockTimeLabel);
+
+ l = new Label(c, SWT.NONE);
+ l.setText("Thread Time: ");
+ GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.CENTER).applyTo(l);
+
+ mThreadTimeLabel = new Label(c, SWT.NONE);
+ GridDataFactory.defaultsFor(mThreadTimeLabel)
+ .grab(true, false)
+ .applyTo(mThreadTimeLabel);
+
+ l = new Label(c, SWT.HORIZONTAL | SWT.SEPARATOR);
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(l);
+
+ l = new Label(c, SWT.NONE);
+ l.setText("Per OpenGL Function Statistics:");
+ l.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(l);
+
+ final Table table = new Table(c, SWT.BORDER | SWT.FULL_SELECTION);
+ GridDataFactory.fillDefaults().grab(true, true).span(2, 1).applyTo(table);
+
+ table.setLinesVisible(true);
+ table.setHeaderVisible(true);
+
+ mStatsTableViewer = new TableViewer(table);
+ mStatsLabelProvider = new StatsLabelProvider();
+ mStatsTableComparator = new StatsTableComparator(1);
+
+ // when a column is selected, sort the table based on that column
+ SelectionListener columnSelectionListener = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ TableColumn tc = (TableColumn) e.widget;
+ String colText = tc.getText();
+ for (int i = 0; i < STATS_TABLE_PROPERTIES.length; i++) {
+ if (STATS_TABLE_PROPERTIES[i].equals(colText)) {
+ mStatsTableComparator.setSortColumn(i);
+ table.setSortColumn(tc);
+ table.setSortDirection(mStatsTableComparator.getDirection());
+ mStatsTableViewer.refresh();
+ break;
+ }
+ }
+ }
+ };
+
+ for (int i = 0; i < STATS_TABLE_PROPERTIES.length; i++) {
+ TableViewerColumn tvc = new TableViewerColumn(mStatsTableViewer, SWT.NONE);
+ tvc.getColumn().setText(STATS_TABLE_PROPERTIES[i]);
+ tvc.setLabelProvider(mStatsLabelProvider);
+ tvc.getColumn().setAlignment(STATS_TABLE_COL_ALIGNMENT[i]);
+ tvc.getColumn().addSelectionListener(columnSelectionListener);
+ }
+ mStatsTableViewer.setContentProvider(new StatsContentProvider());
+ mStatsTableViewer.setInput(null);
+ mStatsTableViewer.setComparator(mStatsTableComparator);
+
+ // resize columns appropriately when the size of the widget changes
+ table.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ int w = table.getClientArea().width;
+
+ for (int i = 0; i < STATS_TABLE_COLWIDTH_RATIOS.length; i++) {
+ table.getColumn(i).setWidth((int) (w * STATS_TABLE_COLWIDTH_RATIOS[i]));
+ }
+ }
+ });
+ }
+
+ @Override
+ public Control getControl() {
+ return mSash;
+ }
+
+ @Override
+ public void setFocus() {
+ }
+
+ public void setSelectedFrame(int frame) {
+ mCurrentFrame = frame;
+
+ updateImageCanvas();
+ updateFrameStats();
+ }
+
+ private void updateFrameStats() {
+ final List<GLCall> calls = mTrace.getGLCallsForFrame(mCurrentFrame);
+
+ Job job = new Job("Update Frame Statistics") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ long wallClockDuration = 0;
+ long threadDuration = 0;
+
+ final Map<Function, PerCallStats> cumulativeStats =
+ new EnumMap<Function, PerCallStats>(Function.class);
+
+ for (GLCall c: calls) {
+ wallClockDuration += c.getWallDuration();
+ threadDuration += c.getThreadDuration();
+
+ PerCallStats stats = cumulativeStats.get(c.getFunction());
+ if (stats == null) {
+ stats = new PerCallStats();
+ }
+
+ stats.count++;
+ stats.threadDuration += c.getThreadDuration();
+ stats.wallDuration += c.getWallDuration();
+
+ cumulativeStats.put(c.getFunction(), stats);
+ }
+
+ final String wallTime = formatMilliSeconds(wallClockDuration);
+ final String threadTime = formatMilliSeconds(threadDuration);
+
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ mWallClockTimeLabel.setText(wallTime);
+ mThreadTimeLabel.setText(threadTime);
+ mStatsTableViewer.setInput(cumulativeStats);
+ }
+ });
+
+ return Status.OK_STATUS;
+ }
+ };
+ job.setUser(true);
+ job.schedule();
+ }
+
+ private String formatMilliSeconds(long nanoSeconds) {
+ double milliSeconds = (double) nanoSeconds / 1000000;
+ return String.format("%.2f ms", milliSeconds); //$NON-NLS-1$
+ }
+
+ private void updateImageCanvas() {
+ int lastCallIndex = mTrace.getFrame(mCurrentFrame).getEndIndex() - 1;
+ if (lastCallIndex >= 0 && lastCallIndex < mTrace.getGLCalls().size()) {
+ GLCall call = mTrace.getGLCalls().get(lastCallIndex);
+ final Image image = mTrace.getImage(call);
+ Display.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ mImageCanvas.setImage(image);
+ }
+ });
+ }
+ }
+
+ /** Cumulative stats maintained for each type of OpenGL Function in a particular frame. */
+ private static class PerCallStats {
+ public int count;
+ public long wallDuration;
+ public long threadDuration;
+ }
+
+ private static class StatsContentProvider implements IStructuredContentProvider {
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ if (inputElement instanceof Map<?, ?>) {
+ return ((Map<?, ?>) inputElement).entrySet().toArray();
+ }
+
+ return null;
+ }
+ }
+
+ private static class StatsLabelProvider extends ColumnLabelProvider {
+ @Override
+ public void update(ViewerCell cell) {
+ Object element = cell.getElement();
+ if (!(element instanceof Map.Entry<?, ?>)) {
+ return;
+ }
+
+ Function f = (Function) ((Map.Entry<?, ?>) element).getKey();
+ PerCallStats stats = (PerCallStats) ((Map.Entry<?, ?>) element).getValue();
+
+ switch (cell.getColumnIndex()) {
+ case 0:
+ cell.setText(f.toString());
+ break;
+ case 1:
+ cell.setText(Integer.toString(stats.count));
+ break;
+ case 2:
+ cell.setText(formatDuration(stats.wallDuration));
+ break;
+ case 3:
+ cell.setText(formatDuration(stats.threadDuration));
+ break;
+ default:
+ // should not happen
+ cell.setText("??"); //$NON-NLS-1$
+ break;
+ }
+ }
+
+ private String formatDuration(long time) {
+ // Max duration is in the 10s of milliseconds = xx,xxx,xxx ns
+ // So we require a format specifier that is 10 characters wide
+ return String.format("%,10d", time); //$NON-NLS-1$
+ }
+ }
+
+ private static class StatsTableComparator extends ViewerComparator {
+ private int mSortColumn;
+ private boolean mDescending = true;
+
+ private StatsTableComparator(int defaultSortColIndex) {
+ mSortColumn = defaultSortColIndex;
+ }
+
+ public void setSortColumn(int index) {
+ if (index == mSortColumn) {
+ // if same column as what we are currently sorting on,
+ // then toggle the direction
+ mDescending = !mDescending;
+ } else {
+ mSortColumn = index;
+ mDescending = true;
+ }
+ }
+
+ public int getDirection() {
+ return mDescending ? SWT.UP : SWT.DOWN;
+ }
+
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ Map.Entry<?, ?> entry1;
+ Map.Entry<?, ?> entry2;
+
+ if (mDescending) {
+ entry1 = (Map.Entry<?, ?>) e1;
+ entry2 = (Map.Entry<?, ?>) e2;
+ } else {
+ entry1 = (Map.Entry<?, ?>) e2;
+ entry2 = (Map.Entry<?, ?>) e1;
+ }
+
+ String k1 = entry1.getKey().toString();
+ String k2 = entry2.getKey().toString();
+
+ PerCallStats stats1 = (PerCallStats) entry1.getValue();
+ PerCallStats stats2 = (PerCallStats) entry2.getValue();
+
+ switch (mSortColumn) {
+ case 0: // function name
+ return String.CASE_INSENSITIVE_ORDER.compare(k1, k2);
+ case 1:
+ return stats1.count - stats2.count;
+ case 2:
+ return (int) (stats1.wallDuration - stats2.wallDuration);
+ case 3:
+ return (int) (stats1.threadDuration - stats2.threadDuration);
+ default:
+ return super.compare(viewer, e1, e2);
+ }
+ }
+ }
+}