From 6f8090a477d752ec6815977c5da799c5b1418bee Mon Sep 17 00:00:00 2001 From: David Li Date: Wed, 6 Apr 2011 18:20:12 -0700 Subject: [PATCH] GLES2Dbg: organize calls into frames and contexts Maintain a current state for each context updated by each call. eglSwapBuffers Begins a new frame; clone current state. Use clone to compute context for a call within that frame later on, in the context tree view. Signed-off-by: David Li --- .../src/com/android/glesv2debugger/Context.java | 239 ++++++++++++---- .../com/android/glesv2debugger/GLServerShader.java | 53 ++-- .../com/android/glesv2debugger/GLServerState.java | 137 ++++++---- .../android/glesv2debugger/GLServerTexture.java | 232 ++++++++++++++++ .../com/android/glesv2debugger/GLServerVertex.java | 214 ++++++++++----- .../com/android/glesv2debugger/MessageData.java | 26 +- .../src/com/android/glesv2debugger/SampleView.java | 300 +++++++++++++-------- .../com/android/glesv2debugger/ShaderEditor.java | 8 +- 8 files changed, 886 insertions(+), 323 deletions(-) create mode 100644 eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerTexture.java diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/Context.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/Context.java index ff096d782..c228df8d9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/Context.java +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/Context.java @@ -21,23 +21,78 @@ 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.lang.reflect.Array; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Set; +class Frame { + final Context startContext; + ArrayList calls = new ArrayList(); + + Frame(final Context context) { + this.startContext = context.clone(); + } +} + +class DebugContext { + final int contextId; + Context currentContext; + ArrayList frames = new ArrayList(128); + private Frame currentFrame; + + DebugContext(final int contextId) { + this.contextId = contextId; + currentContext = new Context(contextId); + frames.add(new Frame(currentContext)); + currentFrame = frames.get(0); + } + + MessageData ProcessMessage(final Message oriMsg) { + currentContext.ProcessMessage(oriMsg); + Message msg = oriMsg; + if (currentContext.processed != null) + msg = currentContext.processed; + currentContext.processed = null; + MessageData msgData = new MessageData(Display.getCurrent(), msg, oriMsg, currentContext); + currentFrame.calls.add(msgData); + if (msg.getFunction() != Function.eglSwapBuffers) + return msgData; + frames.add(currentFrame = new Frame(currentContext)); + return msgData; + } + + Context ComputeContext(final Frame frame, final MessageData call) { + Context ctx = frame.startContext.clone(); + for (int i = 0; i < frame.calls.size(); i++) + if (call == frame.calls.get(i)) + return ctx; + else + ctx.ProcessMessage(frame.calls.get(i).oriMsg); + assert false; + return ctx; + } +} + +/** aggregate of GL states */ public class Context implements Cloneable { public final int contextId; public ArrayList shares = new ArrayList(); // 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]; @@ -48,42 +103,44 @@ public class Context implements Cloneable { shares.add(this); } - // returns instance TODO: return new instance if changed - public Context ProcessMessage(Message msg) { - GLServerVertex newVertex = serverVertex.Process(msg); - if (newVertex != null) { - processed = newVertex.processed; - assert newVertex == serverVertex; - return this; - } - - GLServerShader newShader = serverShader.ProcessMessage(msg); - if (newShader != null) { - assert newShader == serverShader; - return this; + @Override + public Context clone() { + try { + Context copy = (Context) super.clone(); + copy.serverVertex = serverVertex.clone(); + copy.serverShader = serverShader.clone(copy); + copy.serverState = serverState.clone(); + copy.serverTexture = serverTexture.clone(copy); + return copy; + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + assert false; + return null; } + } - GLServerState newState = serverState.ProcessMessage(msg); - if (newState != null) { - if (newState == serverState) - return this; - Context newContext = null; - try { - newContext = (Context) clone(); - } catch (CloneNotSupportedException e) { - assert false; - } - newContext.serverState = newState; - newContext.serverShader.context = newContext; - return newContext; + public void ProcessMessage(Message msg) { + if (serverVertex.Process(msg)) { + processed = serverVertex.processed; + return; } - - return this; + if (serverShader.ProcessMessage(msg)) + return; + if (serverState.ProcessMessage(msg)) + return; + if (serverTexture.ProcessMessage(msg)) + return; } } -class ContextViewProvider extends LabelProvider implements ITreeContentProvider { +class ContextViewProvider extends LabelProvider implements ITreeContentProvider, + ISelectionChangedListener { Context context; + final SampleView sampleView; + + ContextViewProvider(final SampleView sampleView) { + this.sampleView = sampleView; + } @Override public void dispose() { @@ -95,18 +152,54 @@ class ContextViewProvider extends LabelProvider implements ITreeContentProvider return "null"; if (obj instanceof Entry) { Entry entry = (Entry) obj; - if (entry != null) - return entry.name + " = " + 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); + } + 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; + for (int i = 0; i <= sampleView.frameNum.getSelection(); i++) { + if (i == sampleView.current.frames.size()) + return null; + final Frame frame = sampleView.current.frames.get(i); + for (final MessageData msgData : frame.calls) + if (msgData.oriMsg == msg) + return entry.image = msgData.image; + } 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; } @@ -114,6 +207,7 @@ class ContextViewProvider extends LabelProvider implements ITreeContentProvider class Entry { String name; Object obj; + Image image; Entry(String name, Object obj) { this.name = name; @@ -140,6 +234,16 @@ class ContextViewProvider extends LabelProvider implements ITreeContentProvider 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)); + 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); @@ -151,11 +255,11 @@ class ContextViewProvider extends LabelProvider implements ITreeContentProvider + MessageFormatter.Format(msg)); } } else if (entry.obj instanceof SparseArray) { - SparseArray sa = (SparseArray) entry.obj; + SparseArray sa = (SparseArray) entry.obj; for (int i = 0; i < sa.size(); i++) - children.add(new Entry(entry.name + "[" + sa.keyAt(i) + "]", sa.valueAt(i))); + children.add(new Entry("[" + sa.keyAt(i) + "]", sa.valueAt(i))); } else if (entry.obj instanceof Map) { - Set set = ((Map) entry.obj).entrySet(); + 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())); @@ -163,15 +267,14 @@ class ContextViewProvider extends LabelProvider implements ITreeContentProvider } else if (entry.obj instanceof SparseIntArray) { SparseIntArray sa = (SparseIntArray) entry.obj; for (int i = 0; i < sa.size(); i++) - children.add(entry.name + "[" + sa.keyAt(i) + "] = " + sa.valueAt(i)); + children.add("[" + sa.keyAt(i) + "] = " + sa.valueAt(i)); } else if (entry.obj instanceof Collection) { - Collection collection = (Collection) entry.obj; + Collection collection = (Collection) entry.obj; for (Object o : collection) - children.add(new Entry(entry.name, o)); + children.add(new Entry("[?]", o)); } else if (entry.obj.getClass().isArray()) { - Object[] list = (Object[]) entry.obj; - for (Object o : list) - children.add(new Entry(entry.name, o)); + 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) { @@ -196,26 +299,46 @@ class ContextViewProvider extends LabelProvider implements ITreeContentProvider public boolean hasChildren(Object element) { if (element == null) return false; - if (element.getClass().isPrimitive()) + if (!(element instanceof Entry)) return false; - if (element.getClass().equals(String.class)) + Object obj = ((Entry) element).obj; + if (obj == null) return false; - if (element instanceof Entry) { - Entry entry = (Entry) element; - if (entry.obj != null) { - if (entry.obj instanceof SparseArray) - return ((SparseArray) entry.obj).size() > 0; - else if (entry.obj instanceof SparseIntArray) - return ((SparseIntArray) entry.obj).size() > 0; - else if (entry.obj instanceof Collection) - return ((Collection) entry.obj).size() > 0; - else if (entry.obj instanceof Map) - return ((Map) entry.obj).size() > 0; - else if (entry.obj.getClass().isArray()) - return ((Object[]) entry.obj).length > 0; - return entry.obj.getClass().getFields().length > 0; - } - } + 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 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; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerShader.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerShader.java index 4915921bc..cf70993c6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerShader.java +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerShader.java @@ -23,7 +23,7 @@ import java.util.ArrayList; class GLShader implements Cloneable { public final int name; - final GLServerShader context; // the context this was created in + GLServerShader context; // the context this was created in public final GLEnum type; public boolean delete; public ArrayList programs = new ArrayList(); @@ -35,12 +35,12 @@ class GLShader implements Cloneable { this.type = type; } - @Override - // deep copy except for context, which is set afterwards - public Object clone() { + /** deep copy */ + public GLShader clone(final GLServerShader copyContext) { try { GLShader shader = (GLShader) super.clone(); shader.programs = (ArrayList) programs.clone(); + shader.context = copyContext; return shader; } catch (CloneNotSupportedException e) { e.printStackTrace(); @@ -52,7 +52,7 @@ class GLShader implements Cloneable { class GLProgram implements Cloneable { public final int name; - final GLServerShader context; // the context this was created in + GLServerShader context; // the context this was created in public boolean delete; public int vert, frag; @@ -61,11 +61,12 @@ class GLProgram implements Cloneable { this.context = context; } - @Override - // deep copy except for context, which is set afterwards - public Object clone() { + /** deep copy */ + public GLProgram clone(final GLServerShader copyContext) { try { - return super.clone(); + GLProgram copy = (GLProgram) super.clone(); + copy.context = copyContext; + return copy; } catch (CloneNotSupportedException e) { e.printStackTrace(); assert false; @@ -85,22 +86,22 @@ public class GLServerShader implements Cloneable { this.context = context; } - @Override - // deep copy except for context, which is set afterwards - public Object clone() { + /** deep copy */ + public GLServerShader clone(final Context copyContext) { try { GLServerShader copy = (GLServerShader) super.clone(); + copy.context = copyContext; copy.shaders = new SparseArray(shaders.size()); for (int i = 0; i < shaders.size(); i++) - copy.shaders.append(shaders.keyAt(i), (GLShader) shaders.valueAt(i).clone()); + copy.shaders.append(shaders.keyAt(i), shaders.valueAt(i).clone(copy)); copy.programs = new SparseArray(programs.size()); for (int i = 0; i < programs.size(); i++) - copy.programs.append(programs.keyAt(i), (GLProgram) programs.valueAt(i).clone()); + copy.programs.append(programs.keyAt(i), programs.valueAt(i).clone(copy)); if (current != null) - copy.current = (GLProgram) current.clone(); + copy.current = copy.programs.get(current.name); return copy; } catch (CloneNotSupportedException e) { e.printStackTrace(); @@ -109,38 +110,38 @@ public class GLServerShader implements Cloneable { } } - // returns instance if processed - public GLServerShader ProcessMessage(final Message msg) { + /** returns true if processed */ + public boolean ProcessMessage(final Message msg) { boolean oldUiUpdate = uiUpdate; uiUpdate = true; switch (msg.getFunction()) { case glAttachShader: glAttachShader(msg); - return this; + return true; case glCreateProgram: glCreateProgram(msg); - return this; + return true; case glCreateShader: glCreateShader(msg); - return this; + return true; case glDeleteProgram: glDeleteProgram(msg); - return this; + return true; case glDeleteShader: glDeleteShader(msg); - return this; + return true; case glDetachShader: glDetachShader(msg); - return this; + return true; case glShaderSource: glShaderSource(msg); - return this; + return true; case glUseProgram: glUseProgram(msg); - return this; + return true; default: uiUpdate = oldUiUpdate; - return null; + return false; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerState.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerState.java index b79ef3775..adab930f5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerState.java +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerState.java @@ -41,7 +41,12 @@ public class GLServerState implements Cloneable { final Context context; public GLStencilState front = new GLStencilState(), back = new GLStencilState(); public SparseIntArray enableDisables; - public SparseArray lastSetter; // keyed by Function.getNumber() + + /** integer states set via a GL function and GLEnum; keyed by GLEnum.value */ + public SparseArray integers; + + /** states set only via a GL function; keyed by Function.getNumber() */ + public SparseArray lastSetter; GLServerState(final Context context) { this.context = context; @@ -62,36 +67,61 @@ public class GLServerState implements Cloneable { lastSetter.put(Function.glBlendEquationSeparate.getNumber(), null); // glBlendFunc overwrites glBlendFuncSeparate lastSetter.put(Function.glBlendFuncSeparate.getNumber(), null); + lastSetter.put(Function.glClearColor.getNumber(), null); + lastSetter.put(Function.glClearDepthf.getNumber(), null); + lastSetter.put(Function.glClearStencil.getNumber(), null); lastSetter.put(Function.glColorMask.getNumber(), null); + lastSetter.put(Function.glCullFace.getNumber(), null); lastSetter.put(Function.glDepthMask.getNumber(), null); lastSetter.put(Function.glDepthFunc.getNumber(), null); + lastSetter.put(Function.glDepthRangef.getNumber(), null); + lastSetter.put(Function.glFrontFace.getNumber(), null); + lastSetter.put(Function.glLineWidth.getNumber(), null); + lastSetter.put(Function.glPolygonOffset.getNumber(), null); + lastSetter.put(Function.glSampleCoverage.getNumber(), null); lastSetter.put(Function.glScissor.getNumber(), null); lastSetter.put(Function.glStencilMaskSeparate.getNumber(), null); + lastSetter.put(Function.glViewport.getNumber(), null); + + integers = new SparseArray(); + integers.put(GLEnum.GL_PACK_ALIGNMENT.value, null); + integers.put(GLEnum.GL_UNPACK_ALIGNMENT.value, null); } - // returns instance if processed (returns new instance if changed) - public GLServerState ProcessMessage(final Message msg) { + /** returns true if processed */ + public boolean ProcessMessage(final Message msg) { switch (msg.getFunction()) { case glBlendColor: - return Setter(msg); case glBlendEquation: - return Setter(msg); case glBlendEquationSeparate: - return Setter(msg); case glBlendFunc: - return Setter(msg); case glBlendFuncSeparate: - return Setter(msg); + case glClearColor: + case glClearDepthf: + case glClearStencil: case glColorMask: - return Setter(msg); + case glCullFace: case glDepthMask: - return Setter(msg); case glDepthFunc: + case glDepthRangef: return Setter(msg); case glDisable: return EnableDisable(false, msg); case glEnable: return EnableDisable(true, msg); + case glFrontFace: + case glLineWidth: + return Setter(msg); + case glPixelStorei: + if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_PACK_ALIGNMENT) + integers.put(msg.getArg0(), msg); + else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_UNPACK_ALIGNMENT) + integers.put(msg.getArg0(), msg); + else + assert false; + return true; + case glPolygonOffset: + case glSampleCoverage: case glScissor: return Setter(msg); case glStencilFunc: { @@ -104,7 +134,6 @@ public class GLServerState implements Cloneable { case glStencilFuncSeparate: return glStencilFuncSeparate(msg); case glStencilMask: - return Setter(msg); case glStencilMaskSeparate: return Setter(msg); case glStencilOp: { @@ -117,43 +146,42 @@ public class GLServerState implements Cloneable { } case glStencilOpSeparate: return glStencilOpSeparate(msg); + case glViewport: + return Setter(msg); default: - return null; + return false; } } - GLServerState Setter(final Message msg) { - GLServerState newState = (GLServerState) this.clone(); - // TODO: compare for change + boolean Setter(final Message msg) { switch (msg.getFunction()) { case glBlendFunc: - newState.lastSetter.put(Function.glBlendFuncSeparate.getNumber(), msg); + lastSetter.put(Function.glBlendFuncSeparate.getNumber(), msg); break; case glBlendEquation: - newState.lastSetter.put(Function.glBlendEquationSeparate.getNumber(), msg); + lastSetter.put(Function.glBlendEquationSeparate.getNumber(), msg); break; case glStencilMask: - newState.lastSetter.put(Function.glStencilMaskSeparate.getNumber(), msg); + lastSetter.put(Function.glStencilMaskSeparate.getNumber(), msg); break; default: - newState.lastSetter.put(msg.getFunction().getNumber(), msg); + lastSetter.put(msg.getFunction().getNumber(), msg); break; } - return newState; + return true; } - GLServerState EnableDisable(boolean enable, final Message msg) { + boolean EnableDisable(boolean enable, final Message msg) { int index = enableDisables.indexOfKey(msg.getArg0()); assert index >= 0; if ((enableDisables.valueAt(index) != 0) == enable) - return this; - GLServerState newState0 = (GLServerState) this.clone(); - newState0.enableDisables.put(msg.getArg0(), enable ? 1 : 0); - return newState0; + return true; // TODO: redundant + enableDisables.put(msg.getArg0(), enable ? 1 : 0); + return true; } // void StencilFuncSeparate( enum face, enum func, int ref, uint mask ) - GLServerState glStencilFuncSeparate(final Message msg) { + boolean glStencilFuncSeparate(final Message msg) { GLEnum ff = front.func, bf = back.func; int fr = front.ref, br = back.ref; int fm = front.mask, bm = back.mask; @@ -170,19 +198,18 @@ public class GLServerState implements Cloneable { } if (ff == front.func && fr == front.ref && fm == front.mask) if (bf == back.func && br == back.ref && bm == back.mask) - return this; - GLServerState newState = (GLServerState) this.clone(); - newState.front.func = ff; - newState.front.ref = fr; - newState.front.mask = fm; - newState.back.func = bf; - newState.back.ref = br; - newState.back.mask = bm; - return newState; + return true; // TODO: redundant + front.func = ff; + front.ref = fr; + front.mask = fm; + back.func = bf; + back.ref = br; + back.mask = bm; + return true; } // void StencilOpSeparate( enum face, enum sfail, enum dpfail, enum dppass ) - GLServerState glStencilOpSeparate(final Message msg) { + boolean glStencilOpSeparate(final Message msg) { GLEnum fsf = front.sf, fdf = front.df, fdp = front.dp; GLEnum bsf = back.sf, bdf = back.df, bdp = back.dp; final GLEnum face = GLEnum.valueOf(msg.getArg0()); @@ -198,39 +225,41 @@ public class GLServerState implements Cloneable { } if (fsf == front.sf && fdf == front.df && fdp == front.dp) if (bsf == back.sf && bdf == back.df && bdp == back.dp) - return this; - GLServerState newState = (GLServerState) this.clone(); - newState.front.sf = fsf; - newState.front.df = fdf; - newState.front.dp = fdp; - newState.back.sf = bsf; - newState.back.df = bdf; - newState.back.dp = bdp; - return newState; + return true; // TODO: redundant + front.sf = fsf; + front.df = fdf; + front.dp = fdp; + back.sf = bsf; + back.df = bdf; + back.dp = bdp; + return true; } + /** deep copy */ @Override - public Object clone() { + public GLServerState clone() { try { GLServerState newState = (GLServerState) super.clone(); newState.front = (GLStencilState) front.clone(); newState.back = (GLStencilState) back.clone(); newState.enableDisables = new SparseIntArray(enableDisables.size()); - for (int i = 0; i < enableDisables.size(); i++) { - final int key = enableDisables.keyAt(i); - newState.enableDisables.append(key, enableDisables.valueAt(i)); - } + for (int i = 0; i < enableDisables.size(); i++) + newState.enableDisables.append(enableDisables.keyAt(i), + enableDisables.valueAt(i)); + + newState.integers = new SparseArray(integers.size()); + for (int i = 0; i < integers.size(); i++) + newState.integers.append(integers.keyAt(i), integers.valueAt(i)); newState.lastSetter = new SparseArray(lastSetter.size()); - for (int i = 0; i < lastSetter.size(); i++) { - final int key = lastSetter.keyAt(i); - newState.lastSetter.append(key, lastSetter.valueAt(i)); - } + for (int i = 0; i < lastSetter.size(); i++) + newState.lastSetter.append(lastSetter.keyAt(i), lastSetter.valueAt(i)); return newState; } catch (CloneNotSupportedException e) { e.printStackTrace(); + assert false; return null; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerTexture.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerTexture.java new file mode 100644 index 000000000..d246e3f0e --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerTexture.java @@ -0,0 +1,232 @@ +/* + ** 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.sdklib.util.SparseArray; + +import java.nio.ByteBuffer; +import java.util.ArrayList; + +class GLTexture implements Cloneable { + public final int name; + public final GLEnum target; + public ArrayList contentChanges = new ArrayList(); + public GLEnum wrapS = GLEnum.GL_REPEAT, wrapT = GLEnum.GL_REPEAT; + public GLEnum min = GLEnum.GL_NEAREST_MIPMAP_LINEAR; + public GLEnum mag = GLEnum.GL_LINEAR; + public GLEnum format; + public int width, height; + + GLTexture(final int name, final GLEnum target) { + this.name = name; + this.target = target; + } + + @Override + public GLTexture clone() { + try { + GLTexture copy = (GLTexture) super.clone(); + copy.contentChanges = (ArrayList) contentChanges.clone(); + return copy; + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + assert false; + return null; + } + } + + boolean ProcessMessage(final Message msg) { + switch (msg.getFunction()) { + case glCompressedTexImage2D: + case glCopyTexImage2D: + case glTexImage2D: + if (msg.getArg1() == 0) { // level 0 + format = GLEnum.valueOf(msg.getArg2()); + width = msg.getArg3(); + height = msg.getArg4(); + } + //$FALL-THROUGH$ + case glCompressedTexSubImage2D: + case glCopyTexSubImage2D: + case glTexSubImage2D: + case glGenerateMipmap: + contentChanges.add(msg); + break; + default: + assert false; + } + return true; + } + + @Override + public String toString() { + return target.name() + " " + contentChanges.size() + " content change(s)"; + } +} + +public class GLServerTexture implements Cloneable { + Context context; + + public GLEnum activeTexture = GLEnum.GL_TEXTURE0; + public int[] tmu2D = new int[32]; + public int[] tmuCube = new int[32]; + public SparseArray textures = new SparseArray(); + public GLTexture tex2D = null, texCube = null; + + GLServerTexture(final Context context) { + this.context = context; + textures.append(0, null); + } + + public GLServerTexture clone(final Context copyContext) { + try { + GLServerTexture copy = (GLServerTexture) super.clone(); + copy.context = copyContext; + + copy.tmu2D = tmu2D.clone(); + copy.tmuCube = tmuCube.clone(); + + copy.textures = new SparseArray(textures.size()); + for (int i = 0; i < textures.size(); i++) + if (textures.valueAt(i) != null) + copy.textures.append(textures.keyAt(i), textures.valueAt(i).clone()); + else + copy.textures.append(textures.keyAt(i), null); + + if (tex2D != null) + copy.tex2D = copy.textures.get(tex2D.name); + if (texCube != null) + copy.texCube = copy.textures.get(texCube.name); + + return copy; + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + assert false; + return null; + } + } + + public boolean ProcessMessage(final Message msg) { + switch (msg.getFunction()) { + case glActiveTexture: + activeTexture = GLEnum.valueOf(msg.getArg0()); + return true; + case glBindTexture: + return BindTexture(msg.getArg0(), msg.getArg1()); + case glCompressedTexImage2D: + case glCompressedTexSubImage2D: + case glCopyTexImage2D: + case glCopyTexSubImage2D: + case glTexImage2D: + case glTexSubImage2D: + switch (GLEnum.valueOf(msg.getArg0())) { + case GL_TEXTURE_2D: + if (tex2D != null) + return tex2D.ProcessMessage(msg); + return true; + case GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES: + if (texCube != null) + return texCube.ProcessMessage(msg); + return true; + default: + return true; + } + case glDeleteTextures: { + final ByteBuffer names = msg.getData().asReadOnlyByteBuffer(); + names.order(SampleView.targetByteOrder); + for (int i = 0; i < msg.getArg0(); i++) { + final int name = names.getInt(); + if (tex2D != null && tex2D.name == name) + BindTexture(GLEnum.GL_TEXTURE_2D.value, 0); + if (texCube != null && texCube.name == name) + BindTexture(GLEnum.GL_TEXTURE_CUBE_MAP_OES.value, 0); + if (name != 0) + textures.remove(name); + } + return true; + } + case glGenerateMipmap: + if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_TEXTURE_2D && tex2D != null) + return tex2D.ProcessMessage(msg); + else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_TEXTURE_CUBE_MAP_OES + && texCube != null) + return texCube.ProcessMessage(msg); + return true; + case glTexParameteri: + return TexParameter(msg.getArg0(), msg.getArg1(), msg.getArg2()); + case glTexParameterf: + return TexParameter(msg.getArg0(), msg.getArg1(), + (int) Float.intBitsToFloat(msg.getArg2())); + default: + return false; + } + } + + boolean BindTexture(final int target, final int name) { + final int index = activeTexture.value - GLEnum.GL_TEXTURE0.value; + if (GLEnum.valueOf(target) == GLEnum.GL_TEXTURE_2D) { + tex2D = textures.get(name); + if (name != 0 && tex2D == null) + textures.put(name, tex2D = new GLTexture(name, + GLEnum.GL_TEXTURE_2D)); + if (index >= 0 && index < tmu2D.length) + tmu2D[index] = name; + } else if (GLEnum.valueOf(target) == GLEnum.GL_TEXTURE_CUBE_MAP_OES) { + texCube = textures.get(name); + if (name != 0 && texCube == null) + textures.put(name, texCube = new GLTexture(name, + GLEnum.GL_TEXTURE_CUBE_MAP_OES)); + if (index >= 0 && index < tmu2D.length) + tmu2D[index] = name; + } else + assert false; + return true; + } + + boolean TexParameter(final int target, final int pname, final int param) { + GLTexture tex = null; + if (GLEnum.valueOf(target) == GLEnum.GL_TEXTURE_2D) + tex = tex2D; + else if (GLEnum.valueOf(target) == GLEnum.GL_TEXTURE_CUBE_MAP_OES) + tex = texCube; + if (tex == null) + return true; + final GLEnum p = GLEnum.valueOf(param); + switch (GLEnum.valueOf(pname)) { + case GL_TEXTURE_WRAP_S: + tex.wrapS = p; + return true; + case GL_TEXTURE_WRAP_T: + tex.wrapT = p; + return true; + case GL_TEXTURE_MIN_FILTER: + tex.min = p; + return true; + case GL_TEXTURE_MAG_FILTER: + tex.mag = p; + return true; + default: + return true; + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerVertex.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerVertex.java index 4a23bbb1e..a9b5ab85c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerVertex.java +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/GLServerVertex.java @@ -17,17 +17,40 @@ package com.android.glesv2debugger; import com.android.glesv2debugger.DebuggerMessage.Message; +import com.android.sdklib.util.SparseArray; import java.nio.ByteBuffer; -import java.util.HashMap; -class GLBuffer { +class GLBuffer implements Cloneable { + public final int name; public GLEnum usage; public GLEnum target; public ByteBuffer data; + + public GLBuffer(final int name) { + this.name = name; + } + + /** deep copy */ + @Override + public GLBuffer clone() { + try { + GLBuffer copy = (GLBuffer) super.clone(); + if (data != null) { + copy.data = ByteBuffer.allocate(data.capacity()); + data.position(0); + copy.data.put(data); + } + return copy; + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + assert false; + return null; + } + } } -class GLAttribPointer { +class GLAttribPointer implements Cloneable { public int size; // number of values per vertex public GLEnum type; // data type public int stride; // bytes @@ -35,19 +58,32 @@ class GLAttribPointer { public GLBuffer buffer; public boolean normalized; public boolean enabled; -} -public class GLServerVertex { + /** deep copy, re-maps buffer into copyBuffers */ + public GLAttribPointer clone(SparseArray copyBuffers) { + try { + GLAttribPointer copy = (GLAttribPointer) super.clone(); + if (buffer != null) + copy.buffer = copyBuffers.get(buffer.name); + return copy; + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + assert false; + return null; + } + } +} - public HashMap buffers; +public class GLServerVertex implements Cloneable { + public SparseArray buffers = new SparseArray(); public GLBuffer attribBuffer, indexBuffer; // current binding public GLAttribPointer attribPointers[]; public float defaultAttribs[][]; int maxAttrib; public GLServerVertex() { - buffers = new HashMap(); - buffers.put(0, null); + buffers.append(0, null); + // TODO: get MAX_VERTEX_ATTRIBS from server attribPointers = new GLAttribPointer[16]; for (int i = 0; i < attribPointers.length; i++) attribPointers[i] = new GLAttribPointer(); @@ -60,70 +96,102 @@ public class GLServerVertex { } } + /** deep copy */ + @Override + public GLServerVertex clone() { + try { + GLServerVertex copy = (GLServerVertex) super.clone(); + + copy.buffers = new SparseArray(buffers.size()); + for (int i = 0; i < buffers.size(); i++) + if (buffers.valueAt(i) != null) + copy.buffers.append(buffers.keyAt(i), buffers.valueAt(i).clone()); + else + copy.buffers.append(buffers.keyAt(i), null); + + if (attribBuffer != null) + copy.attribBuffer = copy.buffers.get(attribBuffer.name); + if (indexBuffer != null) + copy.indexBuffer = copy.buffers.get(indexBuffer.name); + + copy.attribPointers = new GLAttribPointer[attribPointers.length]; + for (int i = 0; i < attribPointers.length; i++) + copy.attribPointers[i] = attribPointers[i].clone(copy.buffers); + + copy.defaultAttribs = defaultAttribs.clone(); + + return copy; + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + assert false; + return null; + } + } + Message processed = null; // return; glDrawArrays/Elements with fetched data - // returns instance if processed TODO: return new instance if changed - public GLServerVertex Process(final Message msg) { + /** returns true if processed */ + public boolean Process(final Message msg) { processed = null; switch (msg.getFunction()) { case glBindBuffer: glBindBuffer(msg); - return this; + return true; case glBufferData: glBufferData(msg); - return this; + return true; case glBufferSubData: glBufferSubData(msg); - return this; + return true; case glDeleteBuffers: glDeleteBuffers(msg); - return this; + return true; case glDrawArrays: if (msg.hasArg7()) processed = glDrawArrays(msg); - return this; + return true; case glDrawElements: if (msg.hasArg7()) processed = glDrawElements(msg); - return this; + return true; case glDisableVertexAttribArray: glDisableVertexAttribArray(msg); - return this; + return true; case glEnableVertexAttribArray: glEnableVertexAttribArray(msg); - return this; + return true; case glGenBuffers: glGenBuffers(msg); - return this; + return true; case glVertexAttribPointer: glVertexAttribPointer(msg); - return this; + return true; case glVertexAttrib1f: glVertexAttrib1f(msg); - return this; + return true; case glVertexAttrib1fv: glVertexAttrib1fv(msg); - return this; + return true; case glVertexAttrib2f: glVertexAttrib2f(msg); - return this; + return true; case glVertexAttrib2fv: glVertexAttrib2fv(msg); - return this; + return true; case glVertexAttrib3f: glVertexAttrib3f(msg); - return this; + return true; case glVertexAttrib3fv: glVertexAttrib3fv(msg); - return this; + return true; case glVertexAttrib4f: glVertexAttrib4f(msg); - return this; + return true; case glVertexAttrib4fv: glVertexAttrib4fv(msg); - return this; + return true; default: - return null; + return false; } } @@ -181,9 +249,10 @@ public class GLServerVertex { public void glDeleteBuffers(Message msg) { final int n = msg.getArg0(); final ByteBuffer names = msg.getData().asReadOnlyByteBuffer(); + names.order(SampleView.targetByteOrder); for (int i = 0; i < n; i++) { - int name = Integer.reverseBytes(names.getInt()); - GLBuffer buffer = buffers.get(name); + final int name = names.getInt(); + final GLBuffer buffer = buffers.get(name); for (int j = 0; j < attribPointers.length; j++) if (attribPointers[j].buffer == buffer) { attribPointers[j].buffer = null; @@ -204,27 +273,27 @@ public class GLServerVertex { float FetchConvert(final ByteBuffer src, final GLEnum type, final boolean normalized) { if (GLEnum.GL_FLOAT == type) - return Float.intBitsToFloat(Integer.reverseBytes(src.getInt())); + return Float.intBitsToFloat(src.getInt()); else if (GLEnum.GL_UNSIGNED_INT == type) if (normalized) - return (Integer.reverseBytes(src.getInt()) & 0xffffffffL) / (2e32f - 1); + return (src.getInt() & 0xffffffffL) / (2e32f - 1); else - return Integer.reverseBytes(src.getInt()) & 0xffffffffL; + return src.getInt() & 0xffffffffL; else if (GLEnum.GL_INT == type) if (normalized) - return (Integer.reverseBytes(src.getInt()) * 2 + 1) / (2e32f - 1); + return (src.getInt() * 2 + 1) / (2e32f - 1); else - return Integer.reverseBytes(src.getInt()); + return src.getInt(); else if (GLEnum.GL_UNSIGNED_SHORT == type) if (normalized) - return (Short.reverseBytes(src.getShort()) & 0xffff) / (2e16f - 1); + return (src.getShort() & 0xffff) / (2e16f - 1); else - return Short.reverseBytes(src.getShort()) & 0xffff; + return src.getShort() & 0xffff; else if (GLEnum.GL_SHORT == type) if (normalized) - return (Short.reverseBytes(src.getShort()) * 2 + 1) / (2e16f - 1); + return (src.getShort() * 2 + 1) / (2e16f - 1); else - return Short.reverseBytes(src.getShort()); + return src.getShort(); else if (GLEnum.GL_UNSIGNED_BYTE == type) if (normalized) return (src.get() & 0xff) / (2e8f - 1); @@ -237,9 +306,9 @@ public class GLServerVertex { return src.get(); else if (GLEnum.GL_FIXED == type) if (normalized) - return (Integer.reverseBytes(src.getInt()) * 2 + 1) / (2e32f - 1); + return (src.getInt() * 2 + 1) / (2e32f - 1); else - return Integer.reverseBytes(src.getInt()) / (2e16f); + return src.getInt() / (2e16f); else assert false; return 0; @@ -276,7 +345,10 @@ public class GLServerVertex { final ByteBuffer buffer = ByteBuffer.allocate(4 * 4 * maxAttrib * count); ByteBuffer arrays = null; if (msg.hasData()) // server sends user pointer attribs + { arrays = msg.getData().asReadOnlyByteBuffer(); + arrays.order(SampleView.targetByteOrder); + } for (int i = first; i < first + count; i++) Fetch(i, arrays, buffer); assert null == arrays || arrays.remaining() == 0; @@ -294,16 +366,20 @@ public class GLServerVertex { final ByteBuffer buffer = ByteBuffer.allocate(4 * 4 * maxAttrib * count); ByteBuffer arrays = null, index = null; if (msg.hasData()) // server sends user pointer attribs + { arrays = msg.getData().asReadOnlyByteBuffer(); + arrays.order(SampleView.targetByteOrder); + } if (null == indexBuffer) index = arrays; // server also interleaves user pointer indices else { index = indexBuffer.data; + index.order(SampleView.targetByteOrder); index.position(msg.getArg3()); } if (GLEnum.GL_UNSIGNED_SHORT == type) for (int i = 0; i < count; i++) - Fetch(Short.reverseBytes(index.getShort()) & 0xffff, arrays, buffer); + Fetch(index.getShort() & 0xffff, arrays, buffer); else if (GLEnum.GL_UNSIGNED_BYTE == type) for (int i = 0; i < count; i++) Fetch(index.get() & 0xff, arrays, buffer); @@ -324,10 +400,12 @@ public class GLServerVertex { public void glGenBuffers(Message msg) { final int n = msg.getArg0(); final ByteBuffer buffer = msg.getData().asReadOnlyByteBuffer(); + buffer.order(SampleView.targetByteOrder); for (int i = 0; i < n; i++) { - int name = Integer.reverseBytes(buffer.getInt()); - if (!buffers.containsKey(name)) - buffers.put(name, new GLBuffer()); + final int name = buffer.getInt(); + final int index = buffers.indexOfKey(name); + if (index < 0) + buffers.append(name, new GLBuffer(name)); } } @@ -347,53 +425,56 @@ public class GLServerVertex { // void glVertexAttrib1f(GLuint indx, GLfloat x) public void glVertexAttrib1f(Message msg) { - glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(Integer.reverseBytes(msg.getArg1())), + glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()), 0, 0, 1); } // void glVertexAttrib1fv(GLuint indx, const GLfloat* values) public void glVertexAttrib1fv(Message msg) { final ByteBuffer values = msg.getData().asReadOnlyByteBuffer(); + values.order(SampleView.targetByteOrder); glVertexAttrib4f(msg.getArg0(), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt())), + Float.intBitsToFloat(values.getInt()), 0, 0, 1); } // void glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) public void glVertexAttrib2f(Message msg) { - glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(Integer.reverseBytes(msg.getArg1())), - Float.intBitsToFloat(Integer.reverseBytes(msg.getArg2())), 0, 1); + glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()), + Float.intBitsToFloat(msg.getArg2()), 0, 1); } // void glVertexAttrib2fv(GLuint indx, const GLfloat* values) public void glVertexAttrib2fv(Message msg) { final ByteBuffer values = msg.getData().asReadOnlyByteBuffer(); + values.order(SampleView.targetByteOrder); glVertexAttrib4f(msg.getArg0(), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt())), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt())), 0, 1); + Float.intBitsToFloat(values.getInt()), + Float.intBitsToFloat(values.getInt()), 0, 1); } // void glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) public void glVertexAttrib3f(Message msg) { - glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(Integer.reverseBytes(msg.getArg1())), - Float.intBitsToFloat(Integer.reverseBytes(msg.getArg2())), - Float.intBitsToFloat(Integer.reverseBytes(msg.getArg3())), 1); + glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()), + Float.intBitsToFloat(msg.getArg2()), + Float.intBitsToFloat(msg.getArg3()), 1); } // void glVertexAttrib3fv(GLuint indx, const GLfloat* values) public void glVertexAttrib3fv(Message msg) { final ByteBuffer values = msg.getData().asReadOnlyByteBuffer(); + values.order(SampleView.targetByteOrder); glVertexAttrib4f(msg.getArg0(), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt())), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt())), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt())), 1); + Float.intBitsToFloat(values.getInt()), + Float.intBitsToFloat(values.getInt()), + Float.intBitsToFloat(values.getInt()), 1); } public void glVertexAttrib4f(Message msg) { - glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(Integer.reverseBytes(msg.getArg1())), - Float.intBitsToFloat(Integer.reverseBytes(msg.getArg2())), - Float.intBitsToFloat(Integer.reverseBytes(msg.getArg3())), - Float.intBitsToFloat(Integer.reverseBytes(msg.getArg4()))); + glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()), + Float.intBitsToFloat(msg.getArg2()), + Float.intBitsToFloat(msg.getArg3()), + Float.intBitsToFloat(msg.getArg4())); } void glVertexAttrib4f(int indx, float x, float y, float z, float w) { @@ -406,10 +487,11 @@ public class GLServerVertex { // void glVertexAttrib4fv(GLuint indx, const GLfloat* values) public void glVertexAttrib4fv(Message msg) { final ByteBuffer values = msg.getData().asReadOnlyByteBuffer(); + values.order(SampleView.targetByteOrder); glVertexAttrib4f(msg.getArg0(), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt())), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt())), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt())), - Float.intBitsToFloat(Integer.reverseBytes(values.getInt()))); + Float.intBitsToFloat(values.getInt()), + Float.intBitsToFloat(values.getInt()), + Float.intBitsToFloat(values.getInt()), + Float.intBitsToFloat(values.getInt())); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/MessageData.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/MessageData.java index 16eee596b..13965d399 100644 --- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/MessageData.java +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/MessageData.java @@ -26,17 +26,20 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; public class MessageData { - public final Message msg; + public final Message msg, oriMsg; public Image image; // texture public String shader; // shader source public String text; + public String[] columns = new String[3]; public float[] data; public int maxAttrib; // used for formatting data public GLEnum dataType; // could be float, int; mainly for formatting use Context context; // the context before this call - public MessageData(final Device device, final Message msg, final Context context) { + public MessageData(final Device device, final Message msg, final Message oriMsg, + final Context context) { this.msg = msg; + this.oriMsg = oriMsg; this.context = context; image = null; shader = null; @@ -46,21 +49,26 @@ public class MessageData { ImageData imageData = null; if (function != Message.Function.ACK) assert msg.hasTime(); - builder.append(function); + builder.append(columns[0] = function.name()); while (builder.length() < 30) builder.append(' '); - builder.append(String.format("%.3f", msg.getTime())); + columns[1] = String.format("%.3f", msg.getTime()); if (msg.hasClock()) - builder.append(String.format(":%.3f", msg.getClock())); - builder.append(String.format(" 0x%08X", msg.getContextId())); + columns[1] += String.format(":%.3f", msg.getClock()); + builder.append(columns[1]); + + builder.append(" "); + builder.append(String.format("0x%08X", msg.getContextId())); builder.append(" "); + columns[2] = ""; if (msg.getType() == Type.BeforeCall) // incomplete call, client SKIPPED - builder.append("[BeforeCall(AfterCall missing)] "); + columns[2] = "[BeforeCall(AfterCall missing)] "; else if (msg.getType() == Type.AfterGeneratedCall) - builder.append("[AfterGeneratedCall] "); + columns[2] = "[AfterGeneratedCall] "; else assert msg.getType() == Type.AfterCall; - builder.append(MessageFormatter.Format(msg)); + columns[2] += MessageFormatter.Format(msg); + builder.append(columns[2]); switch (function) { case glDrawArrays: // msg was modified by GLServerVertex case glDrawElements: diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/SampleView.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/SampleView.java index 87d97a4a9..9c2a28fef 100644 --- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/SampleView.java +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/SampleView.java @@ -20,6 +20,7 @@ import com.android.glesv2debugger.DebuggerMessage.Message; import com.android.glesv2debugger.DebuggerMessage.Message.Function; import com.android.glesv2debugger.DebuggerMessage.Message.Prop; import com.android.glesv2debugger.DebuggerMessage.Message.Type; +import com.android.sdklib.util.SparseArray; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; @@ -28,27 +29,30 @@ import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.ListViewer; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableLayout; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; @@ -56,11 +60,13 @@ import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Slider; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IActionBars; -import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; @@ -70,9 +76,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.ByteOrder; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Calendar; -import java.util.HashMap; /** * This sample class demonstrates how to plug-in a new workbench view. The view @@ -89,13 +93,14 @@ import java.util.HashMap; *

*/ -public class SampleView extends ViewPart implements Runnable { +public class SampleView extends ViewPart implements Runnable, SelectionListener { public static final ByteOrder targetByteOrder = ByteOrder.LITTLE_ENDIAN; boolean running = false; Thread thread; - MessageQueue messageQueue; - ViewContentProvider viewContentProvider; + MessageQueue messageQueue = new MessageQueue(this); + SparseArray debugContexts = new SparseArray(); + /** * The ID of the view as specified by the extension. */ @@ -104,54 +109,33 @@ public class SampleView extends ViewPart implements Runnable { TabFolder tabFolder; TabItem tabItemText, tabItemImage, tabItemBreakpointOption; TabItem tabItemShaderEditor, tabContextViewer; - ListViewer viewer; + ListViewer viewer; // or TableViewer + Slider frameNum; // scale max cannot overlap min, so max is array size TreeViewer contextViewer; BreakpointOption breakpointOption; ShaderEditor shaderEditor; Canvas canvas; Text text; Action actionConnect; // connect / disconnect - Action doubleClickAction; + Action actionAutoScroll; Action actionFilter; Action actionCapture; Action actionPort; + Action actContext; // for toggling contexts + DebugContext current = null; + Point origin = new Point(0, 0); // for smooth scrolling canvas String[] filters = null; - public HashMap contexts = new HashMap(); - - /* - * The content provider class is responsible for providing objects to the - * view. It can wrap existing objects in adapters or simply return objects - * as-is. These objects may be sensitive to the current input of the view, - * or ignore it and always show the same content (like Task List, for - * example). - */ - class ViewContentProvider implements IStructuredContentProvider { - ArrayList entries = new ArrayList(); - - public void add(final ArrayList msgs) { - entries.addAll(msgs); - viewer.getList().getDisplay().syncExec(new Runnable() { - @Override - public void run() { - viewer.add(msgs.toArray()); - org.eclipse.swt.widgets.ScrollBar bar = viewer - .getList().getVerticalBar(); - if (null != bar && actionAutoScroll.isChecked()) { - bar.setSelection(bar.getMaximum()); - viewer.getList().setSelection( - entries.size() - 1); - // MessageDataSelected(entries.get(entries.size() - 1)); - } - } - }); - } + class ViewContentProvider extends LabelProvider implements IStructuredContentProvider, + ITableLabelProvider { + Frame frame = null; @Override public void inputChanged(Viewer v, Object oldInput, Object newInput) { + frame = (Frame) newInput; } @Override @@ -160,26 +144,36 @@ public class SampleView extends ViewPart implements Runnable { @Override public Object[] getElements(Object parent) { - return entries.toArray(); + return frame.calls.toArray(); } - } - class ViewLabelProvider extends LabelProvider implements - ILabelProvider { @Override public String getText(Object obj) { MessageData msgData = (MessageData) obj; - if (null == msgData) - return obj.toString(); return msgData.text; } @Override public Image getImage(Object obj) { MessageData msgData = (MessageData) obj; - if (null == msgData.image) - return PlatformUI.getWorkbench().getSharedImages() - .getImage(ISharedImages.IMG_OBJ_ELEMENT); + return msgData.image; + } + + @Override + public String getColumnText(Object obj, int index) { + MessageData msgData = (MessageData) obj; + if (index >= msgData.columns.length) + return null; + return msgData.columns[index]; + } + + @Override + public Image getColumnImage(Object obj, int index) { + if (index > -1) + return null; + MessageData msgData = (MessageData) obj; + if (msgData.image == null) + return null; return msgData.image; } } @@ -207,12 +201,7 @@ public class SampleView extends ViewPart implements Runnable { } } - /** - * The constructor. - */ public SampleView() { - messageQueue = new MessageQueue(this); - MessageParserEx messageParserEx = new MessageParserEx(); Message.Builder builder = Message.newBuilder(); messageParserEx.Parse(builder, "glUniform4fv(1,2,[0,1,2,3,4,5,6,7])"); @@ -221,18 +210,64 @@ public class SampleView extends ViewPart implements Runnable { "void glShaderSource(shader=4, count=1, string=\"dksjafhskjahourehghskjg\", length=0x0)"); } - public void CreateView(Composite parent) { - viewer = new ListViewer(parent); + public void CreateLeftPane(Composite parent) { + Composite composite = new Composite(parent, 0); + + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 1; + composite.setLayout(gridLayout); + + frameNum = new Slider(composite, SWT.BORDER | SWT.HORIZONTAL); + frameNum.setMinimum(0); + frameNum.setMaximum(1); + frameNum.setSelection(0); + frameNum.addSelectionListener(this); + + GridData gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.grabExcessHorizontalSpace = true; + gridData.verticalAlignment = SWT.FILL; + frameNum.setLayoutData(gridData); + + Table table = new Table(composite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI + | SWT.FULL_SELECTION); + TableLayout layout = new TableLayout(); + table.setLayout(layout); + table.setLinesVisible(true); + table.setHeaderVisible(true); + String[] headings = { + "Name", "Elapsed (ms)", "Detail" + }; + int[] weights = { + 50, 16, 60 + }; + int[] widths = { + 180, 90, 200 + }; + for (int i = 0; i < headings.length; i++) { + layout.addColumnData(new ColumnWeightData(weights[i], widths[i], + true)); + TableColumn nameCol = new TableColumn(table, SWT.NONE, i); + nameCol.setText(headings[i]); + } + + // viewer = new TableViewer(table); + viewer = new ListViewer(composite, SWT.DEFAULT); viewer.getList().setFont(new Font(viewer.getList().getDisplay(), "Courier", 10, SWT.BOLD)); - viewContentProvider = new ViewContentProvider(); - viewer.setContentProvider(viewContentProvider); - viewer.setLabelProvider(new ViewLabelProvider()); + ViewContentProvider contentProvider = new ViewContentProvider(); + viewer.setContentProvider(contentProvider); + viewer.setLabelProvider(contentProvider); // viewer.setSorter(new NameSorter()); - viewer.setInput(getViewSite()); viewer.setFilters(new ViewerFilter[] { new Filter() }); + gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.grabExcessHorizontalSpace = true; + gridData.verticalAlignment = SWT.FILL; + gridData.grabExcessVerticalSpace = true; + viewer.getControl().setLayoutData(gridData); } /** @@ -241,15 +276,12 @@ public class SampleView extends ViewPart implements Runnable { */ @Override public void createPartControl(Composite parent) { - CreateView(parent); + CreateLeftPane(parent); // Create the help context id for the viewer's control PlatformUI.getWorkbench().getHelpSystem() .setHelp(viewer.getControl(), "GLESv2DebuggerClient.viewer"); - // layoutComposite = new LayoutComposite(parent, 0); - // layoutComposite.setLayout(new FillLayout()); - tabFolder = new TabFolder(parent, SWT.BORDER); text = new Text(tabFolder, SWT.NO_BACKGROUND | SWT.READ_ONLY @@ -276,7 +308,8 @@ public class SampleView extends ViewPart implements Runnable { tabItemShaderEditor.setControl(shaderEditor); contextViewer = new TreeViewer(tabFolder); - ContextViewProvider contextViewProvider = new ContextViewProvider(); + ContextViewProvider contextViewProvider = new ContextViewProvider(this); + contextViewer.addSelectionChangedListener(contextViewProvider); contextViewer.setContentProvider(contextViewProvider); contextViewer.setLabelProvider(contextViewProvider); tabContextViewer = new TabItem(tabFolder, SWT.NONE); @@ -363,7 +396,6 @@ public class SampleView extends ViewPart implements Runnable { makeActions(); hookContextMenu(); - hookDoubleClickAction(); hookSelectionChanged(); contributeToActionBars(); } @@ -473,6 +505,24 @@ public class SampleView extends ViewPart implements Runnable { } }); + actContext = new Action("Context: 0x", Action.AS_DROP_DOWN_MENU) { + @Override + public void run() + { + if (debugContexts.size() < 2) + return; + final String idStr = this.getText().substring( + "Context: 0x".length()); + if (idStr.length() == 0) + return; + final int contextId = Integer.parseInt(idStr, 16); + int index = debugContexts.indexOfKey(contextId); + index = (index + 1) % debugContexts.size(); + ChangeContext(debugContexts.valueAt(index)); + } + }; + manager.add(actContext); + actionPort = new Action("5039", Action.AS_DROP_DOWN_MENU) { @Override @@ -521,30 +571,16 @@ public class SampleView extends ViewPart implements Runnable { }; actionConnect.setText("Connect"); actionConnect.setToolTipText("Connect to debuggee"); - - doubleClickAction = new Action() { - @Override - public void run() { - IStructuredSelection selection = (IStructuredSelection) viewer - .getSelection(); - MessageData msgData = (MessageData) selection.getFirstElement(); - } - }; - } - - private void hookDoubleClickAction() { - viewer.addDoubleClickListener(new IDoubleClickListener() { - @Override - public void doubleClick(DoubleClickEvent event) { - doubleClickAction.run(); - } - }); } void MessageDataSelected(final MessageData msgData) { if (null == msgData) return; - contextViewer.setInput(msgData.context); + if (frameNum.getSelection() == frameNum.getMaximum()) + return; // scale max cannot overlap min, so max is array size + final Frame frame = current.frames.get(frameNum.getSelection()); + final Context context = current.ComputeContext(frame, msgData); + contextViewer.setInput(context); if (null != msgData.image) { canvas.setBackgroundImage(msgData.image); tabFolder.setSelection(tabItemImage); @@ -615,16 +651,32 @@ public class SampleView extends ViewPart implements Runnable { } catch (IOException e1) { showError(e1); } - ArrayList msgs = new ArrayList(); - boolean shaderEditorUpdate = false; + + int newMessages = 0; + + boolean shaderEditorUpdate = false, currentUpdate = false; while (running) { if (!messageQueue.IsRunning()) break; - Message msg = messageQueue.RemoveCompleteMessage(0); - if (msgs.size() > 60 || (msgs.size() > 0 && null == msg)) { - viewContentProvider.add(msgs); - msgs.clear(); + final Message oriMsg = messageQueue.RemoveCompleteMessage(0); + if (newMessages > 60 || (newMessages > 0 && null == oriMsg)) { + newMessages = 0; + + if (currentUpdate || current == null) + getSite().getShell().getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (current == null) + ChangeContext(debugContexts.valueAt(0)); + else + viewer.refresh(false); + frameNum.setMaximum(current.frames.size()); + if (actionAutoScroll.isChecked()) + viewer.getList().setSelection(viewer.getList().getItemCount() - 1); + } + }); + currentUpdate = false; if (shaderEditorUpdate) this.getSite().getShell().getDisplay().syncExec(new Runnable() { @@ -635,7 +687,7 @@ public class SampleView extends ViewPart implements Runnable { }); shaderEditorUpdate = false; } - if (null == msg) { + if (null == oriMsg) { try { Thread.sleep(1); continue; @@ -644,21 +696,20 @@ public class SampleView extends ViewPart implements Runnable { } } - Context context = contexts.get(msg.getContextId()); - if (null == context) { - context = new Context(msg.getContextId()); - contexts.put(msg.getContextId(), context); + DebugContext debugContext = debugContexts.get(oriMsg.getContextId()); + if (debugContext == null) { + debugContext = new DebugContext(oriMsg.getContextId()); + debugContexts.put(oriMsg.getContextId(), debugContext); + } + + final MessageData msgData = debugContext.ProcessMessage(oriMsg); + if (current == debugContext) { + currentUpdate = true; } - Context newContext = context.ProcessMessage(msg); - // TODO: full cloning on change not implemented yet - if (newContext.processed != null) - msg = newContext.processed; - contexts.put(msg.getContextId(), newContext); - shaderEditorUpdate |= newContext.serverShader.uiUpdate; - newContext.serverShader.uiUpdate = false; - - final MessageData msgData = new MessageData(this.getViewSite() - .getShell().getDisplay(), msg, context); + + shaderEditorUpdate |= debugContext.currentContext.serverShader.uiUpdate; + debugContext.currentContext.serverShader.uiUpdate = false; + if (null != writer) { writer.write(msgData.text + "\n"); if (msgData.msg.getFunction() == Function.eglSwapBuffers) { @@ -666,7 +717,7 @@ public class SampleView extends ViewPart implements Runnable { writer.flush(); } } - msgs.add(msgData); + newMessages++; } if (running) ConnectDisconnect(); // error occurred, disconnect @@ -675,4 +726,37 @@ public class SampleView extends ViewPart implements Runnable { writer.close(); } } + + /** can be called from non-UI thread */ + void ChangeContext(final DebugContext newContext) { + getSite().getShell().getDisplay().syncExec(new Runnable() { + @Override + public void run() { + current = newContext; + frameNum.setMaximum(current.frames.size()); + frameNum.setSelection(0); + viewer.setInput(current.frames.get(frameNum.getSelection())); + shaderEditor.Update(); + actContext.setText("Context: 0x" + Integer.toHexString(current.contextId)); + getViewSite().getActionBars().getToolBarManager().update(true); + } + }); + } + + @Override + public void widgetSelected(SelectionEvent e) { + if (e.widget != frameNum) + assert false; + if (current == null) + return; + if (frameNum.getSelection() == current.frames.size()) + return; // scale maximum cannot overlap minimum + Frame frame = current.frames.get(frameNum.getSelection()); + viewer.setInput(frame); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + widgetSelected(e); + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/ShaderEditor.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/ShaderEditor.java index 3aca1c24a..dfb19d274 100644 --- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/ShaderEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/glesv2debugger/ShaderEditor.java @@ -102,7 +102,9 @@ public class ShaderEditor extends Composite implements SelectionListener, Extend public void Update() { list.removeAll(); String progs = "Current Programs: "; - for (Context context : sampleView.contexts.values()) { + for (int j = 0; j < sampleView.debugContexts.size(); j++) { + final Context context = sampleView.debugContexts.valueAt(j).currentContext; + if (context.serverShader.current != null) { progs += context.serverShader.current.name + "(0x"; progs += Integer.toHexString(context.contextId) + ") "; @@ -130,6 +132,7 @@ public class ShaderEditor extends Composite implements SelectionListener, Extend } list.add(builder.toString()); } + } currentPrograms.setText(progs); @@ -336,7 +339,8 @@ public class ShaderEditor extends Composite implements SelectionListener, Extend String[] details = list.getSelection()[0].split("\\s+"); final int contextId = Integer.parseInt(details[0], 16); int name = Integer.parseInt(details[2]); - current = sampleView.contexts.get(contextId).serverShader.shaders.get(name); + current = sampleView.debugContexts.get(contextId).currentContext.serverShader.shaders + .get(name); styledText.setText(current.source); } -- 2.11.0