GLES2Dbg: added reference frame for glReadPixels

Change-Id: I7d6900e3101be61fb7801b3ca2eaea603d917e6b
Signed-off-by: David Li <davidxli@google.com>
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java b/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java
new file mode 100644
index 0000000..3668a56
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java
@@ -0,0 +1,88 @@
+/*
+ ** 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;
+
+public class Context {
+    public int contextId;
+    public GLServerVertex serverVertex = new GLServerVertex();
+    public byte [] readPixelRef = new byte [0];
+
+    public Message ProcessMessage(Message msg)
+    {
+        switch (msg.getFunction()) {
+            case glBindBuffer:
+                serverVertex.glBindBuffer(msg);
+                break;
+            case glBufferData:
+                serverVertex.glBufferData(msg);
+                break;
+            case glBufferSubData:
+                serverVertex.glBufferSubData(msg);
+                break;
+            case glDeleteBuffers:
+                serverVertex.glDeleteBuffers(msg);
+                break;
+            case glDrawArrays:
+                if (msg.hasArg7())
+                    msg = serverVertex.glDrawArrays(msg);
+                break;
+            case glDrawElements:
+                if (msg.hasArg7())
+                    msg = serverVertex.glDrawElements(msg);
+                break;
+            case glDisableVertexAttribArray:
+                serverVertex.glDisableVertexAttribArray(msg);
+                break;
+            case glEnableVertexAttribArray:
+                serverVertex.glEnableVertexAttribArray(msg);
+                break;
+            case glGenBuffers:
+                serverVertex.glGenBuffers(msg);
+                break;
+            case glVertexAttribPointer:
+                serverVertex.glVertexAttribPointer(msg);
+                break;
+            case glVertexAttrib1f:
+                serverVertex.glVertexAttrib1f(msg);
+                break;
+            case glVertexAttrib1fv:
+                serverVertex.glVertexAttrib1fv(msg);
+                break;
+            case glVertexAttrib2f:
+                serverVertex.glVertexAttrib2f(msg);
+                break;
+            case glVertexAttrib2fv:
+                serverVertex.glVertexAttrib2fv(msg);
+                break;
+            case glVertexAttrib3f:
+                serverVertex.glVertexAttrib3f(msg);
+                break;
+            case glVertexAttrib3fv:
+                serverVertex.glVertexAttrib3fv(msg);
+                break;
+            case glVertexAttrib4f:
+                serverVertex.glVertexAttrib4f(msg);
+                break;
+            case glVertexAttrib4fv:
+                serverVertex.glVertexAttrib4fv(msg);
+                break;
+        }
+        return msg;
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java b/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java
index ddc875a..8acf1e5 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java
@@ -483,6 +483,45 @@
       // @@protoc_insertion_point(enum_scope:com.android.glesv2debugger.Message.Type)
     }
     
+    public enum DataType
+        implements com.google.protobuf.Internal.EnumLite {
+      ReferencedImage(0, 0),
+      NonreferencedImage(1, 1),
+      ;
+
+
+      public final int getNumber() { return value; }
+
+      public static DataType valueOf(int value) {
+        switch (value) {
+          case 0: return ReferencedImage;
+          case 1: return NonreferencedImage;
+          default: return null;
+        }
+      }
+
+      public static com.google.protobuf.Internal.EnumLiteMap<DataType>
+          internalGetValueMap() {
+        return internalValueMap;
+      }
+      private static com.google.protobuf.Internal.EnumLiteMap<DataType>
+          internalValueMap =
+            new com.google.protobuf.Internal.EnumLiteMap<DataType>() {
+              public DataType findValueByNumber(int number) {
+                return DataType.valueOf(number)
+      ;        }
+            };
+
+      private final int index;
+      private final int value;
+      private DataType(int index, int value) {
+        this.index = index;
+        this.value = value;
+      }
+
+      // @@protoc_insertion_point(enum_scope:com.android.glesv2debugger.Message.DataType)
+    }
+
     public enum Prop
         implements com.google.protobuf.Internal.EnumLite {
       Capture(0, 0),
@@ -627,6 +666,27 @@
     public boolean hasData() { return hasData; }
     public com.google.protobuf.ByteString getData() { return data_; }
     
+    // optional .com.android.glesv2debugger.Message.DataType data_type = 23;
+    public static final int DATA_TYPE_FIELD_NUMBER = 23;
+    private boolean hasDataType;
+    private com.android.glesv2debugger.DebuggerMessage.Message.DataType dataType_;
+    public boolean hasDataType() { return hasDataType; }
+    public com.android.glesv2debugger.DebuggerMessage.Message.DataType getDataType() { return dataType_; }
+
+    // optional int32 pixel_format = 24;
+    public static final int PIXEL_FORMAT_FIELD_NUMBER = 24;
+    private boolean hasPixelFormat;
+    private int pixelFormat_ = 0;
+    public boolean hasPixelFormat() { return hasPixelFormat; }
+    public int getPixelFormat() { return pixelFormat_; }
+
+    // optional int32 pixel_type = 25;
+    public static final int PIXEL_TYPE_FIELD_NUMBER = 25;
+    private boolean hasPixelType;
+    private int pixelType_ = 0;
+    public boolean hasPixelType() { return hasPixelType; }
+    public int getPixelType() { return pixelType_; }
+
     // optional float time = 11;
     public static final int TIME_FIELD_NUMBER = 11;
     private boolean hasTime;
@@ -651,6 +711,7 @@
     private void initFields() {
       function_ = com.android.glesv2debugger.DebuggerMessage.Message.Function.NEG;
       type_ = com.android.glesv2debugger.DebuggerMessage.Message.Type.BeforeCall;
+      dataType_ = com.android.glesv2debugger.DebuggerMessage.Message.DataType.ReferencedImage;
       prop_ = com.android.glesv2debugger.DebuggerMessage.Message.Prop.Capture;
     }
     public final boolean isInitialized() {
@@ -718,6 +779,15 @@
       if (hasClock()) {
         output.writeFloat(22, getClock());
       }
+      if (hasDataType()) {
+        output.writeEnum(23, getDataType().getNumber());
+      }
+      if (hasPixelFormat()) {
+        output.writeInt32(24, getPixelFormat());
+      }
+      if (hasPixelType()) {
+        output.writeInt32(25, getPixelType());
+      }
     }
     
     private int memoizedSerializedSize = -1;
@@ -798,6 +868,18 @@
         size += com.google.protobuf.CodedOutputStream
           .computeFloatSize(22, getClock());
       }
+      if (hasDataType()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeEnumSize(23, getDataType().getNumber());
+      }
+      if (hasPixelFormat()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(24, getPixelFormat());
+      }
+      if (hasPixelType()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(25, getPixelType());
+      }
       memoizedSerializedSize = size;
       return size;
     }
@@ -987,6 +1069,15 @@
         if (other.hasData()) {
           setData(other.getData());
         }
+        if (other.hasDataType()) {
+          setDataType(other.getDataType());
+        }
+        if (other.hasPixelFormat()) {
+          setPixelFormat(other.getPixelFormat());
+        }
+        if (other.hasPixelType()) {
+          setPixelType(other.getPixelType());
+        }
         if (other.hasTime()) {
           setTime(other.getTime());
         }
@@ -1098,6 +1189,22 @@
               setClock(input.readFloat());
               break;
             }
+            case 184: {
+              int rawValue = input.readEnum();
+              com.android.glesv2debugger.DebuggerMessage.Message.DataType value = com.android.glesv2debugger.DebuggerMessage.Message.DataType.valueOf(rawValue);
+              if (value != null) {
+                setDataType(value);
+              }
+              break;
+            }
+            case 192: {
+              setPixelFormat(input.readInt32());
+              break;
+            }
+            case 200: {
+              setPixelType(input.readInt32());
+              break;
+            }
           }
         }
       }
@@ -1382,6 +1489,63 @@
         return this;
       }
       
+      // optional .com.android.glesv2debugger.Message.DataType data_type = 23;
+      public boolean hasDataType() {
+        return result.hasDataType();
+      }
+      public com.android.glesv2debugger.DebuggerMessage.Message.DataType getDataType() {
+        return result.getDataType();
+      }
+      public Builder setDataType(com.android.glesv2debugger.DebuggerMessage.Message.DataType value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        result.hasDataType = true;
+        result.dataType_ = value;
+        return this;
+      }
+      public Builder clearDataType() {
+        result.hasDataType = false;
+        result.dataType_ = com.android.glesv2debugger.DebuggerMessage.Message.DataType.ReferencedImage;
+        return this;
+      }
+
+      // optional int32 pixel_format = 24;
+      public boolean hasPixelFormat() {
+        return result.hasPixelFormat();
+      }
+      public int getPixelFormat() {
+        return result.getPixelFormat();
+      }
+      public Builder setPixelFormat(int value) {
+        result.hasPixelFormat = true;
+        result.pixelFormat_ = value;
+        return this;
+      }
+      public Builder clearPixelFormat() {
+        result.hasPixelFormat = false;
+        result.pixelFormat_ = 0;
+        return this;
+      }
+
+      // optional int32 pixel_type = 25;
+      public boolean hasPixelType() {
+        return result.hasPixelType();
+      }
+      public int getPixelType() {
+        return result.getPixelType();
+      }
+      public Builder setPixelType(int value) {
+        result.hasPixelType = true;
+        result.pixelType_ = value;
+        return this;
+      }
+      public Builder clearPixelType() {
+        result.hasPixelType = false;
+        result.pixelType_ = 0;
+        return this;
+      }
+
       // optional float time = 11;
       public boolean hasTime() {
         return result.hasTime();
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java
index bd032d0..d87403d 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java
@@ -17,11 +17,15 @@
 package com.android.glesv2debugger;
 
 import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.DataType;
 import com.android.glesv2debugger.DebuggerMessage.Message.Function;
 
 import org.eclipse.swt.graphics.Device;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+
+import java.util.HashMap;
 
 public class MessageData {
     public final Message msg;
@@ -31,8 +35,8 @@
     public float[] data;
     public int maxAttrib; // used for formatting data
     public GLEnum dataType; // could be float, int; mainly for formatting use
-    
-    public MessageData(final Device device, final Message msg) {
+
+    public MessageData(final Device device, final Message msg, final Context context) {
         this.msg = msg;
         image = null;
         shader = null;
@@ -42,7 +46,7 @@
         ImageData imageData = null;
         if (function != Message.Function.ACK)
             assert msg.hasTime();
-        columns = new String [4];
+        columns = new String[4];
         columns[0] = function.toString();
         columns[1] = "";
         if (msg.hasTime())
@@ -63,7 +67,8 @@
             case glShaderSource:
                 shader = msg.getData().toStringUtf8();
                 int index = shader.indexOf('\n');
-                columns[3] += " source: " + shader.substring(0, index >= 0 ? index : shader.length()) + "...";
+                columns[3] += " source: "
+                        + shader.substring(0, index >= 0 ? index : shader.length()) + "...";
                 break;
             case glTexImage2D:
                 if (!msg.hasData())
@@ -85,18 +90,26 @@
                 image = new Image(device, imageData);
                 break;
             case glCopyTexImage2D:
-                imageData = MessageProcessor.ReceiveImage(msg.getArg5(), msg.getArg6(), GLEnum.GL_RGBA.value, GLEnum.GL_UNSIGNED_BYTE.value, msg.getData().toByteArray());
+                imageData = MessageProcessor.ReceiveImage(msg.getArg5(), msg.getArg6(),
+                        GLEnum.GL_RGBA.value, GLEnum.GL_UNSIGNED_BYTE.value, msg.getData()
+                                .toByteArray());
                 image = new Image(device, imageData);
                 break;
             case glCopyTexSubImage2D:
-                imageData = MessageProcessor.ReceiveImage(msg.getArg6(), msg.getArg7(), GLEnum.GL_RGBA.value, GLEnum.GL_UNSIGNED_BYTE.value, msg.getData().toByteArray());
+                imageData = MessageProcessor.ReceiveImage(msg.getArg6(), msg.getArg7(),
+                        GLEnum.GL_RGBA.value, GLEnum.GL_UNSIGNED_BYTE.value, msg.getData()
+                                .toByteArray());
                 image = new Image(device, imageData);
                 break;
             case glReadPixels:
                 if (!msg.hasData())
                     break;
+                if (msg.getDataType() == DataType.ReferencedImage)
+                    MessageProcessor.ref = context.readPixelRef;
                 imageData = MessageProcessor.ReceiveImage(msg.getArg2(), msg.getArg3(),
                         msg.getArg4(), msg.getArg5(), msg.getData().toByteArray());
+                context.readPixelRef = MessageProcessor.ref;
+                MessageProcessor.ref = null;
                 imageData = imageData.scaledTo(imageData.width, -imageData.height);
                 image = new Image(device, imageData);
                 break;
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageProcessor.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageProcessor.java
index a8cdac1..c0ff91f 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageProcessor.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageProcessor.java
@@ -30,6 +30,8 @@
         MessageDialog.openError(null, "MessageProcessor", message);
     }
 
+    public static byte [] ref; // inout; used for glReadPixels
+
     public static ImageData ReceiveImage(int width, int height, int format,
             int type, byte[] data) {
         assert width > 0 && height > 0;
@@ -92,12 +94,20 @@
                 showError("unsupported texture format: " + format);
                 return null;
         }
+
         byte[] pixels = new byte[width * height * (bpp / 8)];
         int decompressed = org.liblzf.CLZF.lzf_decompress(data, data.length, pixels, pixels.length);
         assert decompressed == width * height * (bpp / 8);
 
         PaletteData palette = new PaletteData(redMask, greenMask, blueMask);
-        return new ImageData(width, height, bpp, palette, 1, pixels);
+        if (null != ref) {
+            if (ref.length < decompressed)
+                ref = new byte [width * height * (bpp / 8)];
+            for (int i = 0; i < ref.length; i++)
+                ref[i] ^= pixels[i];
+            return new ImageData(width, height, bpp, palette, 1, ref);
+        } else
+            return new ImageData(width, height, bpp, palette, 1, pixels);
     }
 
     static public float[] ReceiveData(final GLEnum type, final ByteString data) {
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java
index 4a5a750..f0b0325 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java
@@ -36,8 +36,6 @@
     ArrayList<Message> commands = new ArrayList<Message>();
     SampleView sampleView;
 
-    HashMap<Integer, GLServerVertex> serversVertex = new HashMap<Integer, GLServerVertex>();
-
     public MessageQueue(SampleView sampleView) {
         this.sampleView = sampleView;
     }
@@ -159,83 +157,10 @@
                 }
 
                 Message.Builder builder = msg.toBuilder();
-                // builder.mergeFrom(next); seems to merge incorrectly
-                if (next.hasRet())
-                    builder.setRet(next.getRet());
-                if (next.hasTime())
-                    builder.setTime(next.getTime());
-                if (next.hasData())
-                    builder.setData(next.getData());
-                builder.setType(next.getType());
+                builder.mergeFrom(next);
                 msg = builder.build();
             }
 
-            GLServerVertex serverVertex = serversVertex.get(msg.getContextId());
-            if (null == serverVertex) {
-                serverVertex = new GLServerVertex();
-                serversVertex.put(msg.getContextId(), serverVertex);
-            }
-
-            // forward message to synchronize state
-            switch (msg.getFunction()) {
-                case glBindBuffer:
-                    serverVertex.glBindBuffer(msg);
-                    break;
-                case glBufferData:
-                    serverVertex.glBufferData(msg);
-                    break;
-                case glBufferSubData:
-                    serverVertex.glBufferSubData(msg);
-                    break;
-                case glDeleteBuffers:
-                    serverVertex.glDeleteBuffers(msg);
-                    break;
-                case glDrawArrays:
-                    if (msg.hasArg7())
-                        msg = serverVertex.glDrawArrays(msg);
-                    break;
-                case glDrawElements:
-                    if (msg.hasArg7())
-                        msg = serverVertex.glDrawElements(msg);
-                    break;
-                case glDisableVertexAttribArray:
-                    serverVertex.glDisableVertexAttribArray(msg);
-                    break;
-                case glEnableVertexAttribArray:
-                    serverVertex.glEnableVertexAttribArray(msg);
-                    break;
-                case glGenBuffers:
-                    serverVertex.glGenBuffers(msg);
-                    break;
-                case glVertexAttribPointer:
-                    serverVertex.glVertexAttribPointer(msg);
-                    break;
-                case glVertexAttrib1f:
-                    serverVertex.glVertexAttrib1f(msg);
-                    break;
-                case glVertexAttrib1fv:
-                    serverVertex.glVertexAttrib1fv(msg);
-                    break;
-                case glVertexAttrib2f:
-                    serverVertex.glVertexAttrib2f(msg);
-                    break;
-                case glVertexAttrib2fv:
-                    serverVertex.glVertexAttrib2fv(msg);
-                    break;
-                case glVertexAttrib3f:
-                    serverVertex.glVertexAttrib3f(msg);
-                    break;
-                case glVertexAttrib3fv:
-                    serverVertex.glVertexAttrib3fv(msg);
-                    break;
-                case glVertexAttrib4f:
-                    serverVertex.glVertexAttrib4f(msg);
-                    break;
-                case glVertexAttrib4fv:
-                    serverVertex.glVertexAttrib4fv(msg);
-                    break;
-            }
-
             synchronized (complete) {
                 complete.add(msg);
             }
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
index fe2e6e6..66bd989 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
@@ -73,6 +73,7 @@
 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
@@ -112,6 +113,7 @@
 
     Point origin = new Point(0, 0); // for smooth scrolling canvas
     String[] filters = null;
+    public HashMap<Integer, Context> contexts = new HashMap<Integer, Context>();
 
     /*
      * The content provider class is responsible for providing objects to the
@@ -678,7 +680,7 @@
                 break;
 
             Message msg = messageQueue.RemoveMessage(0);
-            if (msgs.size() > 40) {
+            if (msgs.size() > 60 || null == msg) {
                 viewContentProvider.add(msgs);
                 msgs.clear();
             }
@@ -690,8 +692,16 @@
                     showError(e);
                 }
             }
+
+            Context context = contexts.get(msg.getContextId());
+            if (null == context) {
+                context = new Context();
+                contexts.put(msg.getContextId(), context);
+            }
+            msg = context.ProcessMessage(msg);
+
             final MessageData msgData = new MessageData(this.getViewSite()
-                    .getShell().getDisplay(), msg);
+                    .getShell().getDisplay(), msg, context);
             if (null != writer) {
                 writer.write(msgData.columns[0]);
                 for (int i = 0; i < 30 - msgData.columns[0].length(); i++)