| /* |
| ** Copyright 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.glesv2debugger; |
| |
| import com.android.glesv2debugger.DebuggerMessage.Message; |
| import com.android.glesv2debugger.DebuggerMessage.Message.Function; |
| import com.android.sdklib.util.SparseArray; |
| import com.android.sdklib.util.SparseIntArray; |
| |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Display; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.RandomAccessFile; |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Field; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Map; |
| import java.util.Set; |
| |
| class Frame { |
| public final long filePosition; |
| private int callsCount; |
| |
| final Context startContext; |
| private ArrayList<MessageData> calls = new ArrayList<MessageData>(); |
| |
| Frame(final Context context, final long filePosition) { |
| this.startContext = context.clone(); |
| this.filePosition = filePosition; |
| } |
| |
| void Add(final MessageData msgData) { |
| calls.add(msgData); |
| } |
| |
| void IncreaseCallsCount() { |
| callsCount++; |
| } |
| |
| Context ComputeContext(final MessageData call) { |
| Context ctx = startContext.clone(); |
| for (int i = 0; i < calls.size(); i++) |
| if (call == calls.get(i)) |
| return ctx; |
| else |
| ctx.ProcessMessage(calls.get(i).oriMsg); |
| assert false; |
| return ctx; |
| } |
| |
| int Size() { |
| return callsCount; |
| } |
| |
| MessageData Get(final int i) { |
| return calls.get(i); |
| } |
| |
| ArrayList<MessageData> Get() { |
| return calls; |
| } |
| |
| void Unload() { |
| if (calls == null) |
| return; |
| calls.clear(); |
| calls = null; |
| } |
| |
| void Load(final RandomAccessFile file) { |
| if (calls != null && calls.size() == callsCount) |
| return; |
| try { |
| Context ctx = startContext.clone(); |
| calls = new ArrayList<MessageData>(callsCount); |
| final long oriPosition = file.getFilePointer(); |
| file.seek(filePosition); |
| for (int i = 0; i < callsCount; i++) { |
| int len = file.readInt(); |
| if (SampleView.targetByteOrder == ByteOrder.LITTLE_ENDIAN) |
| len = Integer.reverseBytes(len); |
| final byte[] data = new byte[len]; |
| file.read(data); |
| final Message oriMsg = Message.parseFrom(data); |
| final Message msg = ctx.ProcessMessage(oriMsg); |
| final MessageData msgData = new MessageData(Display.getCurrent(), msg, oriMsg, ctx); |
| msgData.attribs = ctx.serverVertex.fetchedAttribs; |
| calls.add(msgData); |
| } |
| file.seek(oriPosition); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| assert false; |
| } |
| } |
| } |
| |
| class DebugContext { |
| boolean uiUpdate = false; |
| final int contextId; |
| Context currentContext; |
| private ArrayList<Frame> frames = new ArrayList<Frame>(128); |
| private Frame lastFrame; |
| private Frame loadedFrame; |
| private RandomAccessFile file; |
| |
| DebugContext(final int contextId) { |
| this.contextId = contextId; |
| currentContext = new Context(contextId); |
| try { |
| file = new RandomAccessFile(Integer.toHexString(contextId) + ".gles2dbg", |
| "rw"); |
| frames.add(new Frame(currentContext, file.getFilePointer())); |
| |
| } catch (FileNotFoundException e) { |
| e.printStackTrace(); |
| assert false; |
| } catch (IOException e) { |
| e.printStackTrace(); |
| assert false; |
| } |
| lastFrame = frames.get(0); |
| loadedFrame = lastFrame; |
| } |
| |
| /** Writes oriMsg to file, and formats into MessageData for current frame */ |
| void ProcessMessage(final Message oriMsg) { |
| synchronized (file) { |
| final byte[] data = oriMsg.toByteArray(); |
| final ByteBuffer len = ByteBuffer.allocate(4); |
| len.order(SampleView.targetByteOrder); |
| len.putInt(data.length); |
| try { |
| if (SampleView.targetByteOrder == ByteOrder.BIG_ENDIAN) |
| file.writeInt(data.length); |
| else |
| file.writeInt(Integer.reverseBytes(data.length)); |
| file.write(data); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| assert false; |
| } |
| } |
| |
| lastFrame.IncreaseCallsCount(); |
| final Message msg = currentContext.ProcessMessage(oriMsg); |
| if (loadedFrame == lastFrame) { |
| final MessageData msgData = new MessageData(Display.getCurrent(), msg, oriMsg, |
| currentContext); |
| msgData.attribs = currentContext.serverVertex.fetchedAttribs; |
| lastFrame.Add(msgData); |
| uiUpdate = true; |
| } |
| if (msg.getFunction() != Function.eglSwapBuffers) |
| return; |
| synchronized (frames) { |
| if (loadedFrame != lastFrame) |
| lastFrame.Unload(); |
| try { |
| frames.add(lastFrame = new Frame(currentContext, file.getFilePointer())); |
| // file.getChannel().force(false); |
| uiUpdate = true; |
| } catch (IOException e) { |
| e.printStackTrace(); |
| assert false; |
| } |
| } |
| return; |
| } |
| |
| Frame GetFrame(int index) { |
| synchronized (frames) { |
| Frame newFrame = frames.get(index); |
| if (loadedFrame != null && loadedFrame != lastFrame && newFrame != loadedFrame) { |
| loadedFrame.Unload(); |
| uiUpdate = true; |
| } |
| loadedFrame = newFrame; |
| synchronized (file) { |
| loadedFrame.Load(file); |
| } |
| return loadedFrame; |
| } |
| } |
| |
| int FrameCount() { |
| synchronized (frames) { |
| return frames.size(); |
| } |
| } |
| } |
| |
| /** aggregate of GL states */ |
| public class Context implements Cloneable { |
| public final int contextId; |
| public ArrayList<Context> shares = new ArrayList<Context>(); // self too |
| public GLServerVertex serverVertex = new GLServerVertex(); |
| public GLServerShader serverShader = new GLServerShader(this); |
| public GLServerState serverState = new GLServerState(this); |
| public GLServerTexture serverTexture = new GLServerTexture(this); |
| |
| byte[] readPixelRef = new byte[0]; |
| |
| public Context(int contextId) { |
| this.contextId = contextId; |
| shares.add(this); |
| } |
| |
| @Override |
| public Context clone() { |
| try { |
| Context copy = (Context) super.clone(); |
| // FIXME: context sharing list clone |
| copy.shares = new ArrayList<Context>(1); |
| copy.shares.add(copy); |
| copy.serverVertex = serverVertex.clone(); |
| copy.serverShader = serverShader.clone(copy); |
| copy.serverState = serverState.clone(); |
| copy.serverTexture = serverTexture.clone(copy); |
| copy.readPixelRef = readPixelRef.clone(); |
| return copy; |
| } catch (CloneNotSupportedException e) { |
| e.printStackTrace(); |
| assert false; |
| return null; |
| } |
| } |
| |
| /** returns processed Message, which could be a new Message */ |
| public Message ProcessMessage(Message msg) { |
| if (serverVertex.Process(msg)) { |
| if (serverVertex.processed != null) |
| return serverVertex.processed; |
| else |
| return msg; |
| } |
| if (serverShader.ProcessMessage(msg)) |
| return msg; |
| if (serverState.ProcessMessage(msg)) |
| return msg; |
| if (serverTexture.ProcessMessage(msg)) |
| return msg; |
| return msg; |
| } |
| } |
| |
| class ContextViewProvider extends LabelProvider implements ITreeContentProvider, |
| ISelectionChangedListener { |
| Context context; |
| final SampleView sampleView; |
| |
| ContextViewProvider(final SampleView sampleView) { |
| this.sampleView = sampleView; |
| } |
| |
| @Override |
| public void dispose() { |
| } |
| |
| @Override |
| public String getText(Object obj) { |
| if (obj == null) |
| return "null"; |
| if (obj instanceof Entry) { |
| Entry entry = (Entry) obj; |
| String objStr = "null (or default)"; |
| if (entry.obj != null) { |
| objStr = entry.obj.toString(); |
| if (entry.obj instanceof Message) |
| objStr = MessageFormatter.Format((Message) entry.obj, false); |
| } |
| return entry.name + " = " + objStr; |
| } |
| return obj.toString(); |
| } |
| |
| @Override |
| public Image getImage(Object obj) { |
| if (!(obj instanceof Entry)) |
| return null; |
| final Entry entry = (Entry) obj; |
| if (!(entry.obj instanceof Message)) |
| return null; |
| final Message msg = (Message) entry.obj; |
| switch (msg.getFunction()) { |
| case glTexImage2D: |
| case glTexSubImage2D: |
| return entry.image = new MessageData(Display.getCurrent(), msg, msg, null).image; |
| case glCopyTexImage2D: |
| case glCopyTexSubImage2D: |
| return null; // TODO: compute context for reference frame |
| default: |
| return null; |
| } |
| } |
| |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| StructuredSelection selection = (StructuredSelection) event |
| .getSelection(); |
| if (null == selection) |
| return; |
| final Object obj = selection.getFirstElement(); |
| if (!(obj instanceof Entry)) |
| return; |
| final Entry entry = (Entry) obj; |
| if (entry.image == null) |
| return; |
| sampleView.tabFolder.setSelection(sampleView.tabItemImage); |
| sampleView.canvas.setBackgroundImage(entry.image); |
| sampleView.canvas.redraw(); |
| } |
| |
| @Override |
| public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { |
| context = (Context) newInput; |
| } |
| |
| class Entry { |
| String name; |
| Object obj; |
| Image image; |
| |
| Entry(String name, Object obj) { |
| this.name = name; |
| this.obj = obj; |
| } |
| } |
| |
| @Override |
| public Object[] getElements(Object inputElement) { |
| if (inputElement != context) |
| return null; |
| return getChildren(new Entry("Context", inputElement)); |
| } |
| |
| @Override |
| public Object[] getChildren(Object parentElement) { |
| if (!(parentElement instanceof Entry)) |
| return null; |
| Entry entry = (Entry) parentElement; |
| ArrayList<Object> children = new ArrayList<Object>(); |
| if (entry.obj == context.serverState.enableDisables) { |
| for (int i = 0; i < context.serverState.enableDisables.size(); i++) { |
| final int key = context.serverState.enableDisables.keyAt(i); |
| final int value = context.serverState.enableDisables.valueAt(i); |
| children.add(GLEnum.valueOf(key).name() + " = " + value); |
| } |
| } else if (entry.obj == context.serverState.integers) { |
| for (int i = 0; i < context.serverState.integers.size(); i++) { |
| final int key = context.serverState.integers.keyAt(i); |
| final Message val = context.serverState.integers.valueAt(i); |
| if (val != null) |
| children.add(GLEnum.valueOf(key).name() + " : " + |
| MessageFormatter.Format(val, false)); |
| else |
| children.add(GLEnum.valueOf(key).name() + " : default"); |
| } |
| } else if (entry.obj == context.serverState.lastSetter) { |
| for (int i = 0; i < context.serverState.lastSetter.size(); i++) { |
| final int key = context.serverState.lastSetter.keyAt(i); |
| final Message msg = context.serverState.lastSetter.valueAt(i); |
| if (msg == null) |
| children.add(Function.valueOf(key).name() + " : default"); |
| else |
| children.add(Function.valueOf(key).name() + " : " |
| + MessageFormatter.Format(msg, false)); |
| } |
| } else if (entry.obj instanceof SparseArray) { |
| SparseArray<?> sa = (SparseArray<?>) entry.obj; |
| for (int i = 0; i < sa.size(); i++) |
| children.add(new Entry("[" + sa.keyAt(i) + "]", sa.valueAt(i))); |
| } else if (entry.obj instanceof Map) { |
| Set<?> set = ((Map<?, ?>) entry.obj).entrySet(); |
| for (Object o : set) { |
| Map.Entry e = (Map.Entry) o; |
| children.add(new Entry(e.getKey().toString(), e.getValue())); |
| } |
| } else if (entry.obj instanceof SparseIntArray) { |
| SparseIntArray sa = (SparseIntArray) entry.obj; |
| for (int i = 0; i < sa.size(); i++) |
| children.add("[" + sa.keyAt(i) + "] = " + sa.valueAt(i)); |
| } else if (entry.obj instanceof Collection) { |
| Collection<?> collection = (Collection<?>) entry.obj; |
| for (Object o : collection) |
| children.add(new Entry("[?]", o)); |
| } else if (entry.obj.getClass().isArray()) { |
| for (int i = 0; i < Array.getLength(entry.obj); i++) |
| children.add(new Entry("[" + i + "]", Array.get(entry.obj, i))); |
| } else { |
| Field[] fields = entry.obj.getClass().getFields(); |
| for (Field f : fields) { |
| try { |
| children.add(new Entry(f.getName(), f.get(entry.obj))); |
| } catch (IllegalArgumentException e) { |
| e.printStackTrace(); |
| } catch (IllegalAccessException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| return children.toArray(); |
| } |
| |
| @Override |
| public Object getParent(Object element) { |
| return null; |
| } |
| |
| @Override |
| public boolean hasChildren(Object element) { |
| if (element == null) |
| return false; |
| if (!(element instanceof Entry)) |
| return false; |
| Object obj = ((Entry) element).obj; |
| if (obj == null) |
| return false; |
| if (obj instanceof SparseArray) |
| return ((SparseArray<?>) obj).size() > 0; |
| else if (obj instanceof SparseIntArray) |
| return ((SparseIntArray) obj).size() > 0; |
| else if (obj instanceof Collection) |
| return ((Collection<?>) obj).size() > 0; |
| else if (obj instanceof Map) |
| return ((Map<?, ?>) obj).size() > 0; |
| else if (obj.getClass().isArray()) |
| return Array.getLength(obj) > 0; |
| else if (obj instanceof Message) |
| return false; |
| else if (IsPrimitive(obj)) |
| return false; |
| else if (obj.getClass().equals(String.class)) |
| return false; |
| else if (obj.getClass().equals(Message.class)) |
| return false; |
| else if (obj instanceof GLEnum) |
| return false; |
| return obj.getClass().getFields().length > 0; |
| } |
| |
| static boolean IsPrimitive(final Object obj) { |
| final Class<? extends Object> c = obj.getClass(); |
| if (c.isPrimitive()) |
| return true; |
| if (c == Integer.class) |
| return true; |
| if (c == Boolean.class) |
| return true; |
| if (c == Float.class) |
| return true; |
| if (c == Short.class) |
| return true; |
| return false; |
| } |
| } |