aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java372
1 files changed, 372 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java
new file mode 100644
index 000000000..faa9561cb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2011 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.GlTracePlugin;
+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.state.GLState;
+import com.android.ide.eclipse.gltrace.state.IGLProperty;
+import com.android.ide.eclipse.gltrace.state.StatePrettyPrinter;
+import com.android.ide.eclipse.gltrace.state.transforms.IStateTransform;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.IPageSite;
+import org.eclipse.ui.part.Page;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A tree view of the OpenGL state. It listens to the current GLCall that is selected
+ * in the Function Trace view, and updates its view to reflect the state as of the selected call.
+ */
+public class StateViewPage extends Page implements ISelectionListener, ISelectionProvider {
+ public static final String ID = "com.android.ide.eclipse.gltrace.views.GLState"; //$NON-NLS-1$
+ private static String sLastUsedPath;
+ private static final ILock sGlStateLock = Job.getJobManager().newLock();
+
+ private GLTrace mTrace;
+ private List<GLCall> mGLCalls;
+
+ /** OpenGL State as of call {@link #mCurrentStateIndex}. */
+ private IGLProperty mState;
+ private int mCurrentStateIndex;
+
+ private String[] TREE_PROPERTIES = { "Name", "Value" };
+ private TreeViewer mTreeViewer;
+ private StateLabelProvider mLabelProvider;
+
+ public StateViewPage(GLTrace trace) {
+ setInput(trace);
+ }
+
+ public void setInput(GLTrace trace) {
+ mTrace = trace;
+ if (trace != null) {
+ mGLCalls = trace.getGLCalls();
+ } else {
+ mGLCalls = null;
+ }
+
+ mState = GLState.createDefaultState();
+ mCurrentStateIndex = -1;
+
+ if (mTreeViewer != null) {
+ mTreeViewer.setInput(mState);
+ mTreeViewer.refresh();
+ }
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ final Tree tree = new Tree(parent, SWT.VIRTUAL | SWT.H_SCROLL | SWT.V_SCROLL);
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(tree);
+
+ tree.setHeaderVisible(true);
+ tree.setLinesVisible(true);
+ tree.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ TreeColumn col1 = new TreeColumn(tree, SWT.LEFT);
+ col1.setText(TREE_PROPERTIES[0]);
+ col1.setWidth(200);
+
+ TreeColumn col2 = new TreeColumn(tree, SWT.LEFT);
+ col2.setText(TREE_PROPERTIES[1]);
+ col2.setWidth(200);
+
+ mTreeViewer = new TreeViewer(tree);
+ mTreeViewer.setContentProvider(new StateContentProvider());
+ mLabelProvider = new StateLabelProvider();
+ mTreeViewer.setLabelProvider(mLabelProvider);
+ mTreeViewer.setInput(mState);
+ mTreeViewer.refresh();
+
+ final IToolBarManager manager = getSite().getActionBars().getToolBarManager();
+ manager.add(new Action("Save to File",
+ PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(
+ ISharedImages.IMG_ETOOL_SAVEAS_EDIT)) {
+ @Override
+ public void run() {
+ saveCurrentState();
+ }
+ });
+ }
+
+ private void saveCurrentState() {
+ final Shell shell = mTreeViewer.getTree().getShell();
+ FileDialog fd = new FileDialog(shell, SWT.SAVE);
+ fd.setFilterExtensions(new String[] { "*.txt" });
+ if (sLastUsedPath != null) {
+ fd.setFilterPath(sLastUsedPath);
+ }
+
+ String path = fd.open();
+ if (path == null) {
+ return;
+ }
+
+ File f = new File(path);
+ sLastUsedPath = f.getParent();
+
+ // export state to f
+ StatePrettyPrinter pp = new StatePrettyPrinter();
+ synchronized (sGlStateLock) {
+ mState.prettyPrint(pp);
+ }
+
+ try {
+ Files.write(pp.toString(), f, Charsets.UTF_8);
+ } catch (IOException e) {
+ ErrorDialog.openError(shell,
+ "Export GL State",
+ "Unexpected error while writing GL state to file.",
+ new Status(Status.ERROR, GlTracePlugin.PLUGIN_ID, e.toString()));
+ }
+ }
+
+ @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 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;
+ }
+
+ final int selectedCallIndex = selectedCall.getIndex();
+
+ // Creation of texture images takes a few seconds on the first run. So run
+ // the update task as an Eclipse job.
+ Job job = new Job("Updating GL State") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ Set<IGLProperty> changedProperties = null;
+
+ try {
+ sGlStateLock.acquire();
+ changedProperties = updateState(mCurrentStateIndex,
+ selectedCallIndex);
+ mCurrentStateIndex = selectedCallIndex;
+ } catch (Exception e) {
+ GlTracePlugin.getDefault().logMessage(
+ "Unexpected error while updating GL State.");
+ GlTracePlugin.getDefault().logMessage(e.getMessage());
+ return new Status(Status.ERROR,
+ GlTracePlugin.PLUGIN_ID,
+ "Unexpected error while updating GL State.",
+ e);
+ } finally {
+ sGlStateLock.release();
+ }
+
+ mLabelProvider.setChangedProperties(changedProperties);
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (!mTreeViewer.getTree().isDisposed()) {
+ mTreeViewer.refresh();
+ }
+ }
+ });
+
+ return Status.OK_STATUS;
+ }
+ };
+ job.setPriority(Job.SHORT);
+ job.schedule();
+ }
+
+ @Override
+ public Control getControl() {
+ if (mTreeViewer == null) {
+ return null;
+ }
+
+ return mTreeViewer.getControl();
+ }
+
+ @Override
+ public void setFocus() {
+ }
+
+ /**
+ * Update GL state from GL call at fromIndex to the call at toIndex.
+ * If fromIndex < toIndex, the GL state will be updated by applying all the transformations
+ * corresponding to calls from (fromIndex + 1) to toIndex (inclusive).
+ * If fromIndex > toIndex, the GL state will be updated by reverting all the calls from
+ * fromIndex (inclusive) to (toIndex + 1).
+ * @return GL state properties that changed as a result of this update.
+ */
+ private Set<IGLProperty> updateState(int fromIndex, int toIndex) {
+ assert fromIndex >= -1 && fromIndex < mGLCalls.size();
+ assert toIndex >= 0 && toIndex < mGLCalls.size();
+
+ if (fromIndex < toIndex) {
+ return applyTransformations(fromIndex, toIndex);
+ } else if (fromIndex > toIndex) {
+ return revertTransformations(fromIndex, toIndex);
+ } else {
+ return Collections.emptySet();
+ }
+ }
+
+ private Set<IGLProperty> applyTransformations(int fromIndex, int toIndex) {
+ int setSizeHint = 3 * (toIndex - fromIndex) + 10;
+ Set<IGLProperty> changedProperties = new HashSet<IGLProperty>(setSizeHint);
+
+ for (int i = fromIndex + 1; i <= toIndex; i++) {
+ GLCall call = mGLCalls.get(i);
+ for (IStateTransform f : call.getStateTransformations()) {
+ try {
+ f.apply(mState);
+ IGLProperty changedProperty = f.getChangedProperty(mState);
+ if (changedProperty != null) {
+ changedProperties.addAll(getHierarchy(changedProperty));
+ }
+ } catch (Exception e) {
+ GlTracePlugin.getDefault().logMessage("Error applying transformations for "
+ + call);
+ GlTracePlugin.getDefault().logMessage(e.toString());
+ }
+ }
+ }
+
+ return changedProperties;
+ }
+
+ private Set<IGLProperty> revertTransformations(int fromIndex, int toIndex) {
+ int setSizeHint = 3 * (fromIndex - toIndex) + 10;
+ Set<IGLProperty> changedProperties = new HashSet<IGLProperty>(setSizeHint);
+
+ for (int i = fromIndex; i > toIndex; i--) {
+ List<IStateTransform> transforms = mGLCalls.get(i).getStateTransformations();
+ // When reverting transformations, iterate from the last to first so that the reversals
+ // are performed in the correct sequence.
+ for (int j = transforms.size() - 1; j >= 0; j--) {
+ IStateTransform f = transforms.get(j);
+ f.revert(mState);
+
+ IGLProperty changedProperty = f.getChangedProperty(mState);
+ if (changedProperty != null) {
+ changedProperties.addAll(getHierarchy(changedProperty));
+ }
+ }
+ }
+
+ return changedProperties;
+ }
+
+ /**
+ * Obtain the list of properties starting from the provided property up to
+ * the root of GL state.
+ */
+ private List<IGLProperty> getHierarchy(IGLProperty changedProperty) {
+ List<IGLProperty> changedProperties = new ArrayList<IGLProperty>(5);
+ changedProperties.add(changedProperty);
+
+ // add the entire parent chain until we reach the root
+ IGLProperty prop = changedProperty;
+ while ((prop = prop.getParent()) != null) {
+ changedProperties.add(prop);
+ }
+
+ return changedProperties;
+ }
+
+ @Override
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ mTreeViewer.addSelectionChangedListener(listener);
+ }
+
+ @Override
+ public ISelection getSelection() {
+ return mTreeViewer.getSelection();
+ }
+
+ @Override
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ mTreeViewer.removeSelectionChangedListener(listener);
+ }
+
+ @Override
+ public void setSelection(ISelection selection) {
+ mTreeViewer.setSelection(selection);
+ }
+}