diff options
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.java | 372 |
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); + } +} |