GLES2Dbg: more tests and minor fixes/improvements

Change-Id: I55c360372623c019da0c1ba2eebbc68f73f0f211
Signed-off-by: David Li <davidxli@google.com>
diff --git a/tools/glesv2debugger/README.android b/tools/glesv2debugger/README.android
new file mode 100644
index 0000000..ae95463
--- /dev/null
+++ b/tools/glesv2debugger/README.android
@@ -0,0 +1,20 @@
+The following is taken from slide 3 & 4 of https://docs.google.com/a/google.com/present/edit?id=0AcZLV3icFYi0ZGZxa3NqZndfMGRqa2tiOXB4&authkey=CMfb8ukI&hl=en
+The spec doc is at https://docs.google.com/a/google.com/document/d/1dsASXCF9Suq8KOGcxwB2mAwgdRlrFj4QhMxkfaRJlA0/edit?hl=en&authkey=CPj4tKkO#
+
+
+Building and Running
+
+Debugger server is linked into EGL, code is in framework/base/opengl/libs/GLES2_dbg and already included in latest master builds, no action needed.
+Use development/tools/glesv2debugger/setup.sh to build and copy the jars: libprotobuf-java-2.3.0-lite, liblzf, sdklib into development/tools/glesv2debugger/lib
+Install Eclipse SDK for Eclipse: Eclipse->Help->Install New Software. Select "All Available Sites" in the "Work with:" drop down, then find "Eclipse SDK". (If Eclipse reports dependency conflicts, try install updates first)
+Debugger client is an Eclipse plug-in, code is at development/tools/glesv2debugger, built in Eclipse
+Optional: build glsl_compiler and copy to plug-in working directory; this is used for shader syntax check
+
+
+"Attaching" to a Process
+
+adb shell setprop debug.egl.debug_proc <process name> before running process. ie: com.example.android.apis
+EGL checks /proc/<proc_id>/cmdline for match during init and sets debug functions in eglMakeCurrent
+EGL will bind to socket and wait for incoming connection, so need to adb forward tcp:5039 tcp:5039. Port can be overridden by adb shell setprop debug.egl.debug_port <port>
+If create socket failed, EGL will try to open /data/local/tmp/dump.gles2dbg for write, and exit when 8MB is written. The relevant properties are ...debug_forceUseFile, ...debug_maxFileSize, and ...debug_filePath
+Now manually start the process on device; on host, open development/tools/glesv2debugger/.project and run/debug as Eclipse application, then Window->Show View->Other->Debug->OpenGL ES 2.0 Debugger, then Connect or Open File
diff --git a/tools/glesv2debugger/generate_MessageParser_java.py b/tools/glesv2debugger/generate_MessageParser_java.py
index 8dcb50f..b6e8282 100755
--- a/tools/glesv2debugger/generate_MessageParser_java.py
+++ b/tools/glesv2debugger/generate_MessageParser_java.py
@@ -67,16 +67,21 @@
 
     String[] getList()
     {
-        assert args.charAt(0) == '{';
         String arg = args;
         args = args.substring(args.lastIndexOf('}') + 1);
-        int comma = args.indexOf(',');
+        final int comma = args.indexOf(',');
         if (comma >= 0)
             args = args.substring(comma + 1).trim();
         else
             args = null;
+
+        final int comment = arg.indexOf('=');
+        if (comment >= 0)
+            arg = arg.substring(comment + 1);
+        arg = arg.trim();
+        assert arg.charAt(0) == '{';
         arg = arg.substring(1, arg.lastIndexOf('}')).trim();
-        return arg.split(",");
+        return arg.split("\\s*,\\s*");
     }
 
     ByteString parseFloats(int count) {
@@ -110,7 +115,7 @@
     }
 
     ByteString parseMatrix(int columns, int count) {
-        return parseFloats(columns * count);
+        return parseFloats(columns * columns * count);
     }
 
     ByteString parseString() {
@@ -127,21 +132,22 @@
 
     String getArgument()
     {
-        int comma = args.indexOf(",");
+        final int comma = args.indexOf(',');
         String arg = null;
         if (comma >= 0)
         {
-            arg = args.substring(0, comma).trim();
-            args = args.substring(comma + 1).trim();
+            arg = args.substring(0, comma);
+            args = args.substring(comma + 1);
         }
         else
         {
             arg = args;
             args = null;
         }
-        if (arg.indexOf("=") >= 0)
-            arg = arg.substring(arg.indexOf("=") + 1);
-        return arg;
+        final int comment = arg.indexOf('=');
+        if (comment >= 0)
+            arg = arg.substring(comment + 1);
+        return arg.trim();
     }
 
     int parseArgument()
@@ -242,8 +248,8 @@
                         assert columns * columns == count
                         assert countArg != ""
                         assert paramType == "GLfloat"
-                        dataSetter = "builder.setData(parseMatrix(%d, %d * builder.getArg%d()));" % (
-                            columns, count, paramNames.index(countArg))
+                        dataSetter = "builder.setData(parseMatrix(%d, builder.getArg%d()));" % (
+                            columns, paramNames.index(countArg))
                     elif annotation == "GLstring":
                         dataSetter = "builder.setData(parseString());"
                     elif paramType.find("void") >= 0:
diff --git a/tools/glesv2debugger/setup.sh b/tools/glesv2debugger/setup.sh
new file mode 100755
index 0000000..839019d
--- /dev/null
+++ b/tools/glesv2debugger/setup.sh
@@ -0,0 +1,35 @@
+source ../../../build/envsetup.sh
+pushd ../../../
+
+# need lunch before building jars
+if [ -z "$TARGET_PRODUCT" ]; then
+    lunch
+fi
+
+pushd external/liblzf/
+mm
+popd
+
+pushd external/protobuf/
+mm
+popd
+
+pushd sdk/sdkmanager/libs/sdklib
+mm
+popd
+
+# glsl_compiler is optional
+# make glsl_compiler -j3
+
+popd
+
+mkdir -p lib
+cp "$ANDROID_HOST_OUT/framework/host-libprotobuf-java-2.3.0-lite.jar" lib/
+cp "$ANDROID_HOST_OUT/framework/liblzf.jar" lib/
+cp "$ANDROID_HOST_OUT/framework/sdklib.jar" lib/
+
+# optional; usually for linux
+#cp "$ANDROID_HOST_OUT/bin/glsl_compiler" ~/
+
+# optional; usually for mac, need to replace eclipse.app with actual path
+#cp "$ANDROID_HOST_OUT/bin/glsl_compiler" eclipse.app/Contents/MacOS
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java b/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java
index e1395d7..122695b 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java
@@ -359,9 +359,12 @@
             case glTexImage2D:
             case glTexSubImage2D:
             case glCopyTexImage2D:
-            case glCopyTexSubImage2D:
-                return entry.image = new MessageData(Display.getCurrent(), msg, null)
-                        .getImage();
+            case glCopyTexSubImage2D: {
+                entry.image = new MessageData(Display.getCurrent(), msg, null).getImage();
+                if (entry.image == null)
+                    return null;
+                return new Image(Display.getCurrent(), entry.image.getImageData().scaledTo(96, 96));
+            }
             default:
                 return null;
         }
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerTexture.java b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerTexture.java
index 4d95684..27676dd 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerTexture.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerTexture.java
@@ -75,7 +75,8 @@
 
     @Override
     public String toString() {
-        return target.name() + " " + contentChanges.size() + " content change(s)";
+        return String.format("%s %s %d*%d %d change(s)", target, format, width, height,
+                contentChanges.size());
     }
 }
 
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerVertex.java b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerVertex.java
index a07f0a4..5f9d513 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerVertex.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerVertex.java
@@ -521,6 +521,8 @@
     }
 
     void glVertexAttrib4f(int indx, float x, float y, float z, float w) {
+        if (indx < 0 || indx >= defaultAttribs.length)
+            return;
         defaultAttribs[indx][0] = x;
         defaultAttribs[indx][1] = y;
         defaultAttribs[indx][2] = z;
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParser.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParser.java
index 8a0c0ec..8536728 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParser.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParser.java
@@ -31,16 +31,21 @@
 
     String[] getList()
     {
-        assert args.charAt(0) == '{';
         String arg = args;
         args = args.substring(args.lastIndexOf('}') + 1);
-        int comma = args.indexOf(',');
+        final int comma = args.indexOf(',');
         if (comma >= 0)
             args = args.substring(comma + 1).trim();
         else
             args = null;
+
+        final int comment = arg.indexOf('=');
+        if (comment >= 0)
+            arg = arg.substring(comment + 1);
+        arg = arg.trim();
+        assert arg.charAt(0) == '{';
         arg = arg.substring(1, arg.lastIndexOf('}')).trim();
-        return arg.split(",");
+        return arg.split("\\s*,\\s*");
     }
 
     ByteString parseFloats(int count) {
@@ -74,7 +79,7 @@
     }
 
     ByteString parseMatrix(int columns, int count) {
-        return parseFloats(columns * count);
+        return parseFloats(columns * columns * count);
     }
 
     ByteString parseString() {
@@ -91,21 +96,22 @@
 
     String getArgument()
     {
-        int comma = args.indexOf(",");
+        final int comma = args.indexOf(',');
         String arg = null;
         if (comma >= 0)
         {
-            arg = args.substring(0, comma).trim();
-            args = args.substring(comma + 1).trim();
+            arg = args.substring(0, comma);
+            args = args.substring(comma + 1);
         }
         else
         {
             arg = args;
             args = null;
         }
-        if (arg.indexOf("=") >= 0)
-            arg = arg.substring(arg.indexOf("=") + 1);
-        return arg;
+        final int comment = arg.indexOf('=');
+        if (comment >= 0)
+            arg = arg.substring(comment + 1);
+        return arg.trim();
     }
 
     int parseArgument()
@@ -634,19 +640,19 @@
                 builder.setArg0(parseArgument()); // GLint location
                 builder.setArg1(parseArgument()); // GLsizei count
                 builder.setArg2(parseArgument()); // GLboolean transpose
-                builder.setData(parseMatrix(2, 4 * builder.getArg1())); // GLfloat value
+                builder.setData(parseMatrix(2, builder.getArg1())); // GLfloat value
                 break;
             case glUniformMatrix3fv:
                 builder.setArg0(parseArgument()); // GLint location
                 builder.setArg1(parseArgument()); // GLsizei count
                 builder.setArg2(parseArgument()); // GLboolean transpose
-                builder.setData(parseMatrix(3, 9 * builder.getArg1())); // GLfloat value
+                builder.setData(parseMatrix(3, builder.getArg1())); // GLfloat value
                 break;
             case glUniformMatrix4fv:
                 builder.setArg0(parseArgument()); // GLint location
                 builder.setArg1(parseArgument()); // GLsizei count
                 builder.setArg2(parseArgument()); // GLboolean transpose
-                builder.setData(parseMatrix(4, 16 * builder.getArg1())); // GLfloat value
+                builder.setData(parseMatrix(4, builder.getArg1())); // GLfloat value
                 break;
             case glUseProgram:
                 builder.setArg0(parseArgument()); // GLuint program
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
index 91e352e..4a8cdc9 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
@@ -192,12 +192,7 @@
     }
 
     public SampleView() {
-        MessageParserEx messageParserEx = new MessageParserEx();
-        Message.Builder builder = Message.newBuilder();
-        messageParserEx.parse(builder, "glUniform4fv(1,2,{0,1,2,3,4,5,6,7})");
-        messageParserEx
-                .parse(builder,
-                        "void glShaderSource(shader=4, count=1, string=\"dksjafhskjahourehghskjg\", length=0x0)");
+
     }
 
     public void createLeftPane(Composite parent) {
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java b/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java
index cdd55c2..c125143 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java
@@ -146,43 +146,45 @@
     void uploadShader() {
         current.source = styledText.getText();
 
-        try {
-            File file = File.createTempFile("shader",
-                    current.type == GLEnum.GL_VERTEX_SHADER ? ".vert" : ".frag");
-            FileWriter fileWriter = new FileWriter(file, false);
-            fileWriter.write(current.source);
-            fileWriter.close();
+        // optional syntax check by glsl_compiler, built from external/mesa3d
+        if (new File("./glsl_compiler").exists())
+            try {
+                File file = File.createTempFile("shader",
+                        current.type == GLEnum.GL_VERTEX_SHADER ? ".vert" : ".frag");
+                FileWriter fileWriter = new FileWriter(file, false);
+                fileWriter.write(current.source);
+                fileWriter.close();
 
-            ProcessBuilder processBuilder = new ProcessBuilder(
-                    "./glsl_compiler", "--glsl-es", file.getAbsolutePath());
-            final Process process = processBuilder.start();
-            InputStream is = process.getInputStream();
-            InputStreamReader isr = new InputStreamReader(is);
-            BufferedReader br = new BufferedReader(isr);
-            String line;
-            String infolog = "";
+                ProcessBuilder processBuilder = new ProcessBuilder(
+                        "./glsl_compiler", "--glsl-es", file.getAbsolutePath());
+                final Process process = processBuilder.start();
+                InputStream is = process.getInputStream();
+                InputStreamReader isr = new InputStreamReader(is);
+                BufferedReader br = new BufferedReader(isr);
+                String line;
+                String infolog = "";
 
-            styledText.setLineBackground(0, styledText.getLineCount(), null);
+                styledText.setLineBackground(0, styledText.getLineCount(), null);
 
-            while ((line = br.readLine()) != null) {
-                infolog += line;
-                if (!line.startsWith("0:"))
-                    continue;
-                String[] details = line.split(":|\\(|\\)");
-                final int ln = Integer.parseInt(details[1]);
-                if (ln > 0) // usually line 0 means errors other than syntax
-                    styledText.setLineBackground(ln - 1, 1,
-                            new Color(Display.getCurrent(), 255, 230, 230));
+                while ((line = br.readLine()) != null) {
+                    infolog += line;
+                    if (!line.startsWith("0:"))
+                        continue;
+                    String[] details = line.split(":|\\(|\\)");
+                    final int ln = Integer.parseInt(details[1]);
+                    if (ln > 0) // usually line 0 means errors other than syntax
+                        styledText.setLineBackground(ln - 1, 1,
+                                new Color(Display.getCurrent(), 255, 230, 230));
+                }
+                file.delete();
+                if (infolog.length() > 0) {
+                    if (!MessageDialog.openConfirm(getShell(),
+                            "Shader Syntax Error, Continue?", infolog))
+                        return;
+                }
+            } catch (IOException e) {
+                sampleView.showError(e);
             }
-            file.delete();
-            if (infolog.length() > 0) {
-                if (!MessageDialog.openConfirm(getShell(),
-                        "Shader Syntax Error, Continue?", infolog))
-                    return;
-            }
-        } catch (IOException e) {
-            sampleView.showError(e);
-        }
 
         // add the initial command, which when read by server will set
         // expectResponse for the message loop and go into message exchange
diff --git a/tools/glesv2debugger/test/com/android/glesv2debugger/MessageParserExTest.java b/tools/glesv2debugger/test/com/android/glesv2debugger/MessageParserExTest.java
new file mode 100644
index 0000000..d2a9a7e
--- /dev/null
+++ b/tools/glesv2debugger/test/com/android/glesv2debugger/MessageParserExTest.java
@@ -0,0 +1,115 @@
+/*
+ ** 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.glesv2debugger.DebuggerMessage.Message.Type;
+import com.google.protobuf.ByteString;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+public class MessageParserExTest {
+    /**
+     * @throws java.lang.Exception
+     */
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    @Test
+    public void testParseFloats() {
+        final MessageParserEx parser = new MessageParserEx();
+        final String args = "{0, 1    ,2,3  }";
+        parser.args = args;
+        final ByteBuffer data = parser.parseFloats(4).asReadOnlyByteBuffer();
+        data.order(SampleView.targetByteOrder);
+        for (int i = 0; i < 4; i++)
+            assertEquals(i, data.getFloat(), 0);
+    }
+
+    @Test
+    public void testParseArgument() {
+        final MessageParserEx parser = new MessageParserEx();
+        final String args = "sdfa   =  GL_VERTEX_SHADER , -5421 ,0x443=0x54f";
+        parser.args = args;
+        assertEquals(GLEnum.GL_VERTEX_SHADER.value, parser.parseArgument());
+        assertEquals(-5421, parser.parseArgument());
+        assertEquals(0x54f, parser.parseArgument());
+    }
+
+    /**
+     * Test method for
+     * {@link com.android.glesv2debugger.MessageParserEx#parse_glShaderSource(com.android.glesv2debugger.DebuggerMessage.Message.Builder)}
+     * .
+     */
+    @Test
+    public void testParse_glShaderSource() {
+        final Message.Builder builder = Message.newBuilder();
+        final MessageParserEx messageParserEx = new MessageParserEx();
+        final String source = "dks \n jafhskjaho { urehg ; } hskjg";
+        messageParserEx.parse(builder, "void glShaderSource ( shader=4, count= 1, "
+                                + "string =\"" + source + "\"  , 0x0)");
+        assertEquals(Function.glShaderSource, builder.getFunction());
+        assertEquals(4, builder.getArg0());
+        assertEquals(1, builder.getArg1());
+        assertEquals(source, builder.getData().toStringUtf8());
+        assertEquals(0, builder.getArg3());
+    }
+
+    @Test
+    public void testParse_glBlendEquation() {
+        assertNotNull(MessageParserEx.instance);
+        final Message.Builder builder = Message.newBuilder();
+        MessageParserEx.instance.parse(builder, "void glBlendEquation ( mode= GL_ADD ) ; ");
+        assertEquals(Function.glBlendEquation, builder.getFunction());
+        assertEquals(GLEnum.GL_ADD.value, builder.getArg0());
+    }
+
+    /** loopback testing of typical generated MessageFormatter and MessageParser */
+    @Test
+    public void testParseFormatterMessage() {
+        final ByteBuffer srcData = ByteBuffer.allocate(4 * 2 * 4);
+        srcData.order(SampleView.targetByteOrder);
+        for (int i = 0; i < 4 * 2; i++)
+            srcData.putFloat(i);
+        srcData.rewind();
+        Message.Builder builder = Message.newBuilder();
+        builder.setContextId(3752).setExpectResponse(false).setType(Type.CompleteCall);
+        builder.setFunction(Function.glUniformMatrix2fv);
+        builder.setArg0(54).setArg1(2).setArg2(0).setData(ByteString.copyFrom(srcData));
+        Message msg = builder.build();
+        builder = msg.toBuilder();
+        String formatted = MessageFormatter.format(msg, false);
+        formatted = formatted.substring(0, formatted.indexOf('(')) + ' ' + builder.getFunction() +
+                formatted.substring(formatted.indexOf('('));
+        Message.Builder parsed = Message.newBuilder();
+        MessageParserEx.instance.parse(parsed, formatted);
+        assertEquals(builder.getFunction(), parsed.getFunction());
+        assertEquals(builder.getArg0(), parsed.getArg0());
+        assertEquals(builder.getArg1(), parsed.getArg1());
+        assertEquals(builder.getArg2(), parsed.getArg2());
+        assertEquals(builder.getData().toStringUtf8(), parsed.getData().toStringUtf8());
+    }
+
+}