Merge "fix memory corruption when retriving GL_MAX_VIEWPORT_DIMS"
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 7d72729..0747efb 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -13,8 +13,8 @@
 	EGL/hooks.cpp 	       \
 	EGL/Loader.cpp 	       \
 #
-LOCAL_STATIC_LIBRARIES += libGLESv2_dbg libprotobuf-cpp-2.3.0-lite liblzf
-LOCAL_SHARED_LIBRARIES += libcutils libutils libstlport
+
+LOCAL_SHARED_LIBRARIES += libcutils libutils libGLESv2_dbg
 LOCAL_LDLIBS := -lpthread -ldl
 LOCAL_MODULE:= libEGL
 LOCAL_LDFLAGS += -Wl,--exclude-libs=ALL
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 6474c87..9cf7223 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -46,6 +46,7 @@
 #include "egl_impl.h"
 #include "Loader.h"
 #include "glesv2dbg.h"
+#include "egl_tls.h"
 
 #define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
 
@@ -58,7 +59,7 @@
 static char const * const gVendorString     = "Android";
 static char const * const gVersionString    = "1.4 Android META-EGL";
 static char const * const gClientApiString  = "OpenGL ES";
-static char const * const gExtensionString  = 
+static char const * const gExtensionString  =
         "EGL_KHR_image "
         "EGL_KHR_image_base "
         "EGL_KHR_image_pixmap "
@@ -221,18 +222,15 @@
 struct egl_context_t : public egl_object_t
 {
     typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
-    
+
     egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
-            int impl, egl_connection_t const* cnx, int version) 
-    : egl_object_t(dpy), dpy(dpy), context(context), config(config), read(0), draw(0), 
-      impl(impl), cnx(cnx), version(version), dbg(NULL)
+            int impl, egl_connection_t const* cnx, int version)
+    : egl_object_t(dpy), dpy(dpy), context(context), config(config), read(0), draw(0),
+      impl(impl), cnx(cnx), version(version)
     {
     }
     ~egl_context_t()
     {
-        if (dbg)
-            DestroyDbgContext(dbg);
-        dbg = NULL;
     }
     EGLDisplay                  dpy;
     EGLContext                  context;
@@ -242,7 +240,6 @@
     int                         impl;
     egl_connection_t const*     cnx;
     int                         version;
-    DbgContext *                dbg;
 };
 
 struct egl_image_t : public egl_object_t
@@ -277,15 +274,6 @@
 typedef egl_image_t::Ref    ImageRef;
 typedef egl_sync_t::Ref     SyncRef;
 
-struct tls_t
-{
-    tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE) { }
-    EGLint      error;
-    EGLContext  ctx;
-    EGLBoolean  logCallWithNoContext;
-};
-
-
 // ----------------------------------------------------------------------------
 
 static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
@@ -323,7 +311,7 @@
     int propertyLevel = atoi(value);
     int applicationLevel = gEGLApplicationTraceLevel;
     gEGLTraceLevel = propertyLevel > applicationLevel ? propertyLevel : applicationLevel;
-    
+
     property_get("debug.egl.debug_proc", value, "");
     long pid = getpid();
     char procPath[128] = {};
@@ -336,14 +324,20 @@
         {
             if (!strcmp(value, cmdline))
                 gEGLDebugLevel = 1;
-        }    
+        }
         fclose(file);
     }
-    
+
     if (gEGLDebugLevel > 0)
     {
         property_get("debug.egl.debug_port", value, "5039");
-        StartDebugServer(atoi(value));
+        const unsigned short port = (unsigned short)atoi(value);
+        property_get("debug.egl.debug_forceUseFile", value, "0");
+        const bool forceUseFile = (bool)atoi(value);
+        property_get("debug.egl.debug_maxFileSize", value, "8");
+        const unsigned int maxFileSize = atoi(value) << 20;
+        property_get("debug.egl.debug_filePath", value, "/data/local/tmp/dump.gles2dbg");
+        StartDebugServer(port, forceUseFile, maxFileSize, value);
     }
 }
 
@@ -586,7 +580,7 @@
 }
 
 static inline
-egl_surface_t* get_surface(EGLSurface surface) {   
+egl_surface_t* get_surface(EGLSurface surface) {
     return egl_to_native_cast<egl_surface_t>(surface);
 }
 
@@ -595,11 +589,6 @@
     return egl_to_native_cast<egl_context_t>(context);
 }
 
-DbgContext * getDbgContextThreadSpecific()
-{
-    return get_context(getContext())->dbg;
-}
-
 static inline
 egl_image_t* get_image(EGLImageKHR image) {
     return egl_to_native_cast<egl_image_t>(image);
@@ -1442,10 +1431,12 @@
         loseCurrent(cur_c);
 
         if (ctx != EGL_NO_CONTEXT) {
-            if (!c->dbg && gEGLDebugLevel > 0)
-                c->dbg = CreateDbgContext(c->version, c->cnx->hooks[c->version]);
             setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
             setContext(ctx);
+            tls_t * const tls = getTLS();
+            if (!tls->dbg && gEGLDebugLevel > 0)
+                tls->dbg = CreateDbgContext(gEGLThreadLocalStorageKey, c->version,
+                                            c->cnx->hooks[c->version]);
             _c.acquire();
             _r.acquire();
             _d.acquire();
diff --git a/opengl/libs/GLES2_dbg/Android.mk b/opengl/libs/GLES2_dbg/Android.mk
index fc40799..853cce6 100644
--- a/opengl/libs/GLES2_dbg/Android.mk
+++ b/opengl/libs/GLES2_dbg/Android.mk
@@ -11,7 +11,7 @@
     src/server.cpp \
     src/vertex.cpp
 
-LOCAL_C_INCLUDES :=	\
+LOCAL_C_INCLUDES := \
     $(LOCAL_PATH) \
     $(LOCAL_PATH)/../ \
     external/stlport/stlport \
@@ -21,7 +21,8 @@
 
 #LOCAL_CFLAGS += -O0 -g -DDEBUG -UNDEBUG
 LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI
-
+LOCAL_STATIC_LIBRARIES := libprotobuf-cpp-2.3.0-lite liblzf
+LOCAL_SHARED_LIBRARIES := libcutils libutils libstlport
 ifeq ($(TARGET_ARCH),arm)
 	LOCAL_CFLAGS += -fstrict-aliasing
 endif
@@ -43,4 +44,4 @@
 LOCAL_MODULE:= libGLESv2_dbg
 LOCAL_MODULE_TAGS := optional
 
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/libs/GLES2_dbg/generate_api_cpp.py b/opengl/libs/GLES2_dbg/generate_api_cpp.py
index 66c110f..aeba213 100755
--- a/opengl/libs/GLES2_dbg/generate_api_cpp.py
+++ b/opengl/libs/GLES2_dbg/generate_api_cpp.py
@@ -26,36 +26,36 @@
         return line.replace(annotation, "*")
     else:
         return line
-    
+
 def generate_api(lines):
     externs = []
     i = 0
     # these have been hand written
-    skipFunctions = ["glReadPixels", "glDrawArrays", "glDrawElements"]
-    
+    skipFunctions = ["glDrawArrays", "glDrawElements"]
+
     # these have an EXTEND_Debug_* macro for getting data
-    extendFunctions = ["glCopyTexImage2D", "glCopyTexSubImage2D", "glShaderSource",
-"glTexImage2D", "glTexSubImage2D"]
-    
+    extendFunctions = ["glCopyTexImage2D", "glCopyTexSubImage2D", "glReadPixels",
+"glShaderSource", "glTexImage2D", "glTexSubImage2D"]
+
     # these also needs to be forwarded to DbgContext
     contextFunctions = ["glUseProgram", "glEnableVertexAttribArray", "glDisableVertexAttribArray", 
 "glVertexAttribPointer", "glBindBuffer", "glBufferData", "glBufferSubData", "glDeleteBuffers",]
-    
+
     for line in lines:
         if line.find("API_ENTRY(") >= 0: # a function prototype
             returnType = line[0: line.find(" API_ENTRY(")]
             functionName = line[line.find("(") + 1: line.find(")")] #extract GL function name
             parameterList = line[line.find(")(") + 2: line.find(") {")]
-            
+
             #if line.find("*") >= 0:
             #    extern = "%s Debug_%s(%s);" % (returnType, functionName, parameterList)
             #    externs.append(extern)
             #    continue
-            
+
             if functionName in skipFunctions:
                 sys.stderr.write("!\n! skipping function '%s'\n!\n" % (functionName))
                 continue
-                
+
             parameters = parameterList.split(',')
             paramIndex = 0
             if line.find("*") >= 0 and (line.find("*") < line.find(":") or line.find("*") > line.rfind(":")): # unannotated pointer
@@ -65,21 +65,21 @@
                     sys.stderr.write("%s should be hand written\n" % (extern))
                     print "// FIXME: this function has pointers, it should be hand written"
                     externs.append(extern)
-                
+
             print "%s Debug_%s(%s)\n{" % (returnType, functionName, RemoveAnnotation(parameterList))
             print "    glesv2debugger::Message msg;"
-    
+
             if parameterList == "void":
                 parameters = []
             arguments = ""
             paramNames = []
             inout = ""
             getData = ""
-            
+
             callerMembers = ""
             setCallerMembers = ""
             setMsgParameters = ""
-            
+
             for parameter in parameters:
                 const = parameter.find("const")
                 parameter = parameter.replace("const", "")
@@ -107,7 +107,7 @@
                         annotation = "strlen(%s)" % (paramName)
                     else:
                         count = int(annotation)
-            
+
                     setMsgParameters += "    msg.set_arg%d(ToInt(%s));\n" % (paramIndex, paramName)
                     if paramType.find("void") >= 0:
                         getData += "    msg.mutable_data()->assign(reinterpret_cast<const char *>(%s), %s * sizeof(char));" % (paramName, annotation)
@@ -127,7 +127,7 @@
                 paramIndex += 1
                 callerMembers += "        %s %s;\n" % (paramType, paramName)
                 setCallerMembers += "    caller.%s = %s;\n" % (paramName, paramName)
-            
+
             print "    struct : public FunctionCall {"
             print callerMembers
             print "        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {"
@@ -141,6 +141,11 @@
             if inout in ["out", "inout"]:
                 print "            msg.set_time((systemTime(timeMode) - c0) * 1e-6f);"
                 print "        " + getData
+            if functionName in extendFunctions:
+                print "\
+#ifdef EXTEND_AFTER_CALL_Debug_%s\n\
+            EXTEND_AFTER_CALL_Debug_%s;\n\
+#endif" % (functionName, functionName)
             if functionName in contextFunctions:
                 print "            getDbgContextThreadSpecific()->%s(%s);" % (functionName, arguments)
             if returnType == "void":
@@ -157,7 +162,10 @@
             if inout in ["in", "inout"]:
                 print getData
             if functionName in extendFunctions:
-                print "    EXTEND_Debug_%s;" % (functionName) 
+                print "\
+#ifdef EXTEND_Debug_%s\n\
+    EXTEND_Debug_%s;\n\
+#endif" % (functionName, functionName)
             print "    int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_%s);"\
                 % (functionName)
             if returnType != "void":
@@ -166,8 +174,8 @@
                 else:
                     print "    return reinterpret_cast<%s>(ret);" % (returnType)
             print "}\n"
-                        
-            
+
+
     print "// FIXME: the following functions should be written by hand"
     for extern in externs:
         print extern
@@ -189,18 +197,23 @@
  ** See the License for the specific language governing permissions and
  ** limitations under the License.
  */
- 
+
 // auto generated by generate_api_cpp.py
 
+#include <utils/Debug.h>
+
 #include "src/header.h"
 #include "src/api.h"
 
-template<typename T> static int ToInt(const T & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (int &)t; }
-template<typename T> static T FromInt(const int & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (T &)t; }
-"""    
+template<typename T> static int ToInt(const T & t)
+{
+    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(T) == sizeof(int));
+    return (int &)t;
+}
+"""
     lines = open("gl2_api_annotated.in").readlines()
     generate_api(lines)
     #lines = open("gl2ext_api.in").readlines()
     #generate_api(lines)
-            
+
 
diff --git a/opengl/libs/GLES2_dbg/generate_caller_cpp.py b/opengl/libs/GLES2_dbg/generate_caller_cpp.py
index eac2292..ee4208d 100755
--- a/opengl/libs/GLES2_dbg/generate_caller_cpp.py
+++ b/opengl/libs/GLES2_dbg/generate_caller_cpp.py
@@ -177,7 +177,6 @@
 {
     LOGD("GenerateCall function=%u", cmd.function());
     const int * ret = prevRet; // only some functions have return value
-    gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
     nsecs_t c0 = systemTime(timeMode);
     switch (cmd.function()) {""")
     
diff --git a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
index 466c447..914ea24 100755
--- a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
+++ b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
@@ -70,41 +70,43 @@
 """)
 
     i = 0;
-    
+
     lines = open("gl2_api_annotated.in").readlines()
     i = generate_gl_entries(output, lines, i)
     output.write("        // end of GL functions\n")
-    
+
     #lines = open("gl2ext_api.in").readlines()
     #i = generate_gl_entries(output, lines, i)
     #output.write("        // end of GL EXT functions\n")
-    
+
     lines = open("../EGL/egl_entries.in").readlines()
     i = generate_egl_entries(output, lines, i)
     output.write("        // end of GL EXT functions\n")
-    
+
     output.write("        ACK = %d;\n" % (i))
     i += 1
-    
+
     output.write("        NEG = %d;\n" % (i))
     i += 1
-    
+
     output.write("        CONTINUE = %d;\n" % (i))
     i += 1
-    
+
     output.write("        SKIP = %d;\n" % (i))
     i += 1
-    
+
     output.write("        SETPROP = %d;\n" % (i))
     i += 1
-    
+
     output.write("""    }
     required Function function = 2 [default = NEG]; // type/function of message
     enum Type
     {
         BeforeCall = 0;
         AfterCall = 1;
-        Response = 2; // currently used for misc messages
+        AfterGeneratedCall = 2;
+        Response = 3; // currently used for misc messages
+        CompleteCall = 4; // BeforeCall and AfterCall merged
     }
     required Type type = 3;
     required bool expect_response = 4;
@@ -125,16 +127,21 @@
         ReferencedImage = 0; // for image sourced from ReadPixels
         NonreferencedImage = 1; // for image sourced from ReadPixels
     };
-    optional DataType data_type = 23; // most data types can be inferred from function
-    optional int32 pixel_format = 24; // used for image data if format and type 
-    optional int32 pixel_type = 25;   //     cannot be determined from arg 
-    
+    // most data types can be inferred from function
+    optional DataType data_type = 23;
+    // these are used for image data when they cannot be determined from args
+    optional int32 pixel_format = 24;
+    optional int32 pixel_type = 25;
+    optional int32 image_width = 26;
+    optional int32 image_height = 27;
+
     optional float time = 11; // duration of previous GL call (ms)
     enum Prop
     {
-        Capture = 0; // arg0 = true | false
+        CaptureDraw = 0; // arg0 = number of glDrawArrays/Elements to glReadPixels
         TimeMode = 1; // arg0 = SYSTEM_TIME_* in utils/Timers.h
         ExpectResponse = 2; // arg0 = enum Function, arg1 = true/false
+        CaptureSwap = 3; // arg0 = number of eglSwapBuffers to glReadPixels
     };
     optional Prop prop = 21; // used with SETPROP, value in arg0
     optional float clock = 22; // wall clock in seconds
@@ -142,6 +149,6 @@
 """)
 
     output.close()
-    
+
     os.system("aprotoc --cpp_out=src --java_out=../../../../../development/tools/glesv2debugger/src debugger_message.proto")
     os.system('mv -f "src/debugger_message.pb.cc" "src/debugger_message.pb.cpp"')
diff --git a/opengl/libs/GLES2_dbg/src/api.cpp b/opengl/libs/GLES2_dbg/src/api.cpp
index 130ca7e..c483547 100644
--- a/opengl/libs/GLES2_dbg/src/api.cpp
+++ b/opengl/libs/GLES2_dbg/src/api.cpp
@@ -16,11 +16,16 @@
  
 // auto generated by generate_api_cpp.py
 
+#include <utils/Debug.h>
+
 #include "src/header.h"
 #include "src/api.h"
 
-template<typename T> static int ToInt(const T & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (int &)t; }
-template<typename T> static T FromInt(const int & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (T &)t; }
+template<typename T> static int ToInt(const T & t)
+{
+    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(T) == sizeof(int));
+    return (int &)t;
+}
 
 void Debug_glActiveTexture(GLenum texture)
 {
@@ -592,6 +597,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+#ifdef EXTEND_AFTER_CALL_Debug_glCopyTexImage2D
+            EXTEND_AFTER_CALL_Debug_glCopyTexImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -613,7 +621,9 @@
     msg.set_arg6(height);
     msg.set_arg7(border);
 
+#ifdef EXTEND_Debug_glCopyTexImage2D
     EXTEND_Debug_glCopyTexImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glCopyTexImage2D);
 }
 
@@ -632,6 +642,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+#ifdef EXTEND_AFTER_CALL_Debug_glCopyTexSubImage2D
+            EXTEND_AFTER_CALL_Debug_glCopyTexSubImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -653,7 +666,9 @@
     msg.set_arg6(width);
     msg.set_arg7(height);
 
+#ifdef EXTEND_Debug_glCopyTexSubImage2D
     EXTEND_Debug_glCopyTexSubImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glCopyTexSubImage2D);
 }
 
@@ -2164,6 +2179,49 @@
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glPolygonOffset);
 }
 
+void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)
+{
+    glesv2debugger::Message msg;
+    struct : public FunctionCall {
+        GLint x;
+        GLint y;
+        GLsizei width;
+        GLsizei height;
+        GLenum format;
+        GLenum type;
+        GLvoid* pixels;
+
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            _c->glReadPixels(x, y, width, height, format, type, pixels);
+#ifdef EXTEND_AFTER_CALL_Debug_glReadPixels
+            EXTEND_AFTER_CALL_Debug_glReadPixels;
+#endif
+            return 0;
+        }
+    } caller;
+    caller.x = x;
+    caller.y = y;
+    caller.width = width;
+    caller.height = height;
+    caller.format = format;
+    caller.type = type;
+    caller.pixels = pixels;
+
+    msg.set_arg0(x);
+    msg.set_arg1(y);
+    msg.set_arg2(width);
+    msg.set_arg3(height);
+    msg.set_arg4(format);
+    msg.set_arg5(type);
+    msg.set_arg6(ToInt(pixels));
+
+    // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glReadPixels
+    EXTEND_Debug_glReadPixels;
+#endif
+    int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glReadPixels);
+}
+
 void Debug_glReleaseShaderCompiler(void)
 {
     glesv2debugger::Message msg;
@@ -2297,6 +2355,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glShaderSource(shader, count, string, length);
+#ifdef EXTEND_AFTER_CALL_Debug_glShaderSource
+            EXTEND_AFTER_CALL_Debug_glShaderSource;
+#endif
             return 0;
         }
     } caller;
@@ -2311,7 +2372,9 @@
     msg.set_arg3(ToInt(length));
 
     // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glShaderSource
     EXTEND_Debug_glShaderSource;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glShaderSource);
 }
 
@@ -2472,6 +2535,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+#ifdef EXTEND_AFTER_CALL_Debug_glTexImage2D
+            EXTEND_AFTER_CALL_Debug_glTexImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -2496,7 +2562,9 @@
     msg.set_arg8(ToInt(pixels));
 
     // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glTexImage2D
     EXTEND_Debug_glTexImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glTexImage2D);
 }
 
@@ -2616,6 +2684,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+#ifdef EXTEND_AFTER_CALL_Debug_glTexSubImage2D
+            EXTEND_AFTER_CALL_Debug_glTexSubImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -2640,7 +2711,9 @@
     msg.set_arg8(ToInt(pixels));
 
     // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glTexSubImage2D
     EXTEND_Debug_glTexSubImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glTexSubImage2D);
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/api.h b/opengl/libs/GLES2_dbg/src/api.h
index b9fc341..0b227bc 100644
--- a/opengl/libs/GLES2_dbg/src/api.h
+++ b/opengl/libs/GLES2_dbg/src/api.h
@@ -16,19 +16,29 @@
 
 #define EXTEND_Debug_glCopyTexImage2D \
     DbgContext * const dbg = getDbgContextThreadSpecific(); \
-    GLint readFormat, readType; \
-    dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat); \
-    dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType); \
-    unsigned readSize = GetBytesPerPixel(readFormat, readType) * width * height; \
-    void * readData = dbg->GetReadPixelsBuffer(readSize); \
-    dbg->hooks->gl.glReadPixels(x, y, width, height, readFormat, readType, readData); \
+    void * readData = dbg->GetReadPixelsBuffer(4 * width * height); \
+    /* pick easy format for client to convert */ \
+    dbg->hooks->gl.glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, readData); \
     dbg->CompressReadPixelBuffer(msg.mutable_data()); \
     msg.set_data_type(msg.ReferencedImage); \
-    msg.set_pixel_format(readFormat); \
-    msg.set_pixel_type(readType);
+    msg.set_pixel_format(GL_RGBA); \
+    msg.set_pixel_type(GL_UNSIGNED_BYTE);
 
 #define EXTEND_Debug_glCopyTexSubImage2D EXTEND_Debug_glCopyTexImage2D
 
+#define EXTEND_AFTER_CALL_Debug_glReadPixels \
+    { \
+        DbgContext * const dbg = getDbgContextThreadSpecific(); \
+        if (dbg->IsReadPixelBuffer(pixels)) { \
+            dbg->CompressReadPixelBuffer(msg.mutable_data()); \
+            msg.set_data_type(msg.ReferencedImage); \
+        } else { \
+            const unsigned int size = width * height * GetBytesPerPixel(format, type); \
+            dbg->Compress(pixels, size, msg.mutable_data()); \
+            msg.set_data_type(msg.NonreferencedImage); \
+        } \
+    }
+
 #define EXTEND_Debug_glShaderSource \
     std::string * const data = msg.mutable_data(); \
     for (unsigned i = 0; i < count; i++) \
diff --git a/opengl/libs/GLES2_dbg/src/caller.cpp b/opengl/libs/GLES2_dbg/src/caller.cpp
index 9992f05..6b72751 100644
--- a/opengl/libs/GLES2_dbg/src/caller.cpp
+++ b/opengl/libs/GLES2_dbg/src/caller.cpp
@@ -105,7 +105,6 @@
 {
     LOGD("GenerateCall function=%u", cmd.function());
     const int * ret = prevRet; // only some functions have return value
-    gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
     nsecs_t c0 = systemTime(timeMode);
     switch (cmd.function()) {    case glesv2debugger::Message_Function_glActiveTexture:
         dbg->hooks->gl.glActiveTexture(
@@ -772,7 +771,7 @@
     msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_function(cmd.function());
-    msg.set_type(glesv2debugger::Message_Type_AfterCall);
+    msg.set_type(glesv2debugger::Message_Type_AfterGeneratedCall);
     return ret;
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/caller.h b/opengl/libs/GLES2_dbg/src/caller.h
index 5447757..e8111b3 100644
--- a/opengl/libs/GLES2_dbg/src/caller.h
+++ b/opengl/libs/GLES2_dbg/src/caller.h
@@ -138,7 +138,9 @@
         const glesv2debugger::Message & cmd,
         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    GLint params = -1;
+    dbg->hooks->gl.glGetProgramiv(cmd.arg0(), cmd.arg1(), &params);
+    msg.mutable_data()->append(reinterpret_cast<char *>(&params), sizeof(params));
     return prevRet;
 }
 
@@ -146,7 +148,10 @@
         const glesv2debugger::Message & cmd,
         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    const GLsizei bufSize = static_cast<GLsizei>(dbg->GetBufferSize());
+    GLsizei length = -1;
+    dbg->hooks->gl.glGetProgramInfoLog(cmd.arg0(), bufSize, &length, dbg->GetBuffer());
+    msg.mutable_data()->append(dbg->GetBuffer(), length);
     return prevRet;
 }
 
@@ -162,7 +167,9 @@
                                         const glesv2debugger::Message & cmd,
                                         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    GLint params = -1;
+    dbg->hooks->gl.glGetShaderiv(cmd.arg0(), cmd.arg1(), &params);
+    msg.mutable_data()->append(reinterpret_cast<char *>(&params), sizeof(params));
     return prevRet;
 }
 
@@ -170,7 +177,10 @@
         const glesv2debugger::Message & cmd,
         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    const GLsizei bufSize = static_cast<GLsizei>(dbg->GetBufferSize());
+    GLsizei length = -1;
+    dbg->hooks->gl.glGetShaderInfoLog(cmd.arg0(), bufSize, &length, dbg->GetBuffer());
+    msg.mutable_data()->append(dbg->GetBuffer(), length);
     return prevRet;
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
index cc7336c..fe93874 100644
--- a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
+++ b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "header.h"
+#include "egl_tls.h"
 
 extern "C"
 {
@@ -24,11 +25,23 @@
 namespace android
 {
 
+static pthread_key_t sEGLThreadLocalStorageKey = -1;
+
+DbgContext * getDbgContextThreadSpecific()
+{
+    tls_t* tls = (tls_t*)pthread_getspecific(sEGLThreadLocalStorageKey);
+    return tls->dbg;
+}
+
 DbgContext::DbgContext(const unsigned version, const gl_hooks_t * const hooks,
-                       const unsigned MAX_VERTEX_ATTRIBS)
+                       const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat,
+                       const GLenum readType)
         : lzf_buf(NULL), lzf_readIndex(0), lzf_refSize(0), lzf_refBufSize(0)
         , version(version), hooks(hooks)
         , MAX_VERTEX_ATTRIBS(MAX_VERTEX_ATTRIBS)
+        , readFormat(readFormat), readType(readType)
+        , readBytesPerPixel(GetBytesPerPixel(readFormat, readType))
+        , captureSwap(0), captureDraw(0)
         , vertexAttribs(new VertexAttrib[MAX_VERTEX_ATTRIBS])
         , hasNonVBOAttribs(false), indexBuffers(NULL), indexBuffer(NULL)
         , program(0), maxAttrib(0)
@@ -47,13 +60,18 @@
     free(lzf_ref[1]);
 }
 
-DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks)
+DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey,
+                              const unsigned version, const gl_hooks_t * const hooks)
 {
+    sEGLThreadLocalStorageKey = EGLThreadLocalStorageKey;
     assert(version < 2);
     assert(GL_NO_ERROR == hooks->gl.glGetError());
     GLint MAX_VERTEX_ATTRIBS = 0;
     hooks->gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &MAX_VERTEX_ATTRIBS);
-    return new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS);
+    GLint readFormat, readType;
+    hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
+    hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
+    return new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS, readFormat, readType);
 }
 
 void DestroyDbgContext(DbgContext * const dbg)
@@ -113,6 +131,7 @@
 {
     if (!lzf_buf)
         lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
+    assert(lzf_buf);
     const uint32_t totalDecompSize = in_len;
     outStr->append((const char *)&totalDecompSize, sizeof(totalDecompSize));
     for (unsigned int i = 0; i < in_len; i += LZF_CHUNK_SIZE) {
@@ -135,8 +154,10 @@
     if (lzf_refBufSize < size + 8) {
         lzf_refBufSize = size + 8;
         lzf_ref[0] = (unsigned *)realloc(lzf_ref[0], lzf_refBufSize);
+        assert(lzf_ref[0]);
         memset(lzf_ref[0], 0, lzf_refBufSize);
         lzf_ref[1] = (unsigned *)realloc(lzf_ref[1], lzf_refBufSize);
+        assert(lzf_ref[1]);
         memset(lzf_ref[1], 0, lzf_refBufSize);
     }
     if (lzf_refSize != size) // need to clear unused ref to maintain consistency
@@ -151,6 +172,7 @@
 
 void DbgContext::CompressReadPixelBuffer(std::string * const outStr)
 {
+    assert(lzf_ref[0] && lzf_ref[1]);
     unsigned * const ref = lzf_ref[lzf_readIndex ^ 1];
     unsigned * const src = lzf_ref[lzf_readIndex];
     for (unsigned i = 0; i < lzf_refSize / sizeof(*ref) + 1; i++)
@@ -158,13 +180,34 @@
     Compress(ref, lzf_refSize, outStr);
 }
 
+char * DbgContext::GetBuffer()
+{
+    if (!lzf_buf)
+        lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
+    assert(lzf_buf);
+    return lzf_buf;
+}
+
+unsigned int DbgContext::GetBufferSize()
+{
+    if (!lzf_buf)
+        lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
+    assert(lzf_buf);
+    if (lzf_buf)
+        return LZF_CHUNK_SIZE;
+    else
+        return 0;
+}
+
 void DbgContext::glUseProgram(GLuint program)
 {
     while (GLenum error = hooks->gl.glGetError())
-        LOGD("DbgContext::glUseProgram: before glGetError() = 0x%.4X", error);
-
+        LOGD("DbgContext::glUseProgram(%u): before glGetError() = 0x%.4X",
+             program, error);
     this->program = program;
-
+    maxAttrib = 0;
+    if (program == 0)
+        return;
     GLint activeAttributes = 0;
     hooks->gl.glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &activeAttributes);
     maxAttrib = 0;
@@ -202,9 +245,9 @@
             maxAttrib = slot;
     }
     delete name;
-
     while (GLenum error = hooks->gl.glGetError())
-        LOGD("DbgContext::glUseProgram: after glGetError() = 0x%.4X", error);
+        LOGD("DbgContext::glUseProgram(%u): after glGetError() = 0x%.4X",
+             program, error);
 }
 
 static bool HasNonVBOAttribs(const DbgContext * const ctx)
@@ -254,14 +297,16 @@
 
 void DbgContext::glEnableVertexAttribArray(GLuint index)
 {
-    assert(index < MAX_VERTEX_ATTRIBS);
+    if (index >= MAX_VERTEX_ATTRIBS)
+        return;
     vertexAttribs[index].enabled = true;
     hasNonVBOAttribs = HasNonVBOAttribs(this);
 }
 
 void DbgContext::glDisableVertexAttribArray(GLuint index)
 {
-    assert(index < MAX_VERTEX_ATTRIBS);
+    if (index >= MAX_VERTEX_ATTRIBS)
+        return;
     vertexAttribs[index].enabled = false;
     hasNonVBOAttribs = HasNonVBOAttribs(this);
 }
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
index 046c954..40478c3 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
@@ -436,6 +436,8 @@
     case 0:
     case 1:
     case 2:
+    case 3:
+    case 4:
       return true;
     default:
       return false;
@@ -445,7 +447,9 @@
 #ifndef _MSC_VER
 const Message_Type Message::BeforeCall;
 const Message_Type Message::AfterCall;
+const Message_Type Message::AfterGeneratedCall;
 const Message_Type Message::Response;
+const Message_Type Message::CompleteCall;
 const Message_Type Message::Type_MIN;
 const Message_Type Message::Type_MAX;
 const int Message::Type_ARRAYSIZE;
@@ -472,6 +476,7 @@
     case 0:
     case 1:
     case 2:
+    case 3:
       return true;
     default:
       return false;
@@ -479,9 +484,10 @@
 }
 
 #ifndef _MSC_VER
-const Message_Prop Message::Capture;
+const Message_Prop Message::CaptureDraw;
 const Message_Prop Message::TimeMode;
 const Message_Prop Message::ExpectResponse;
+const Message_Prop Message::CaptureSwap;
 const Message_Prop Message::Prop_MIN;
 const Message_Prop Message::Prop_MAX;
 const int Message::Prop_ARRAYSIZE;
@@ -506,6 +512,8 @@
 const int Message::kDataTypeFieldNumber;
 const int Message::kPixelFormatFieldNumber;
 const int Message::kPixelTypeFieldNumber;
+const int Message::kImageWidthFieldNumber;
+const int Message::kImageHeightFieldNumber;
 const int Message::kTimeFieldNumber;
 const int Message::kPropFieldNumber;
 const int Message::kClockFieldNumber;
@@ -545,6 +553,8 @@
   data_type_ = 0;
   pixel_format_ = 0;
   pixel_type_ = 0;
+  image_width_ = 0;
+  image_height_ = 0;
   time_ = 0;
   prop_ = 0;
   clock_ = 0;
@@ -606,6 +616,8 @@
   if (_has_bits_[16 / 32] & (0xffu << (16 % 32))) {
     pixel_format_ = 0;
     pixel_type_ = 0;
+    image_width_ = 0;
+    image_height_ = 0;
     time_ = 0;
     prop_ = 0;
     clock_ = 0;
@@ -790,7 +802,7 @@
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
                  input, &time_)));
-          _set_bit(18);
+          _set_bit(20);
         } else {
           goto handle_uninterpreted;
         }
@@ -905,7 +917,7 @@
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
                  input, &clock_)));
-          _set_bit(20);
+          _set_bit(22);
         } else {
           goto handle_uninterpreted;
         }
@@ -960,6 +972,38 @@
         } else {
           goto handle_uninterpreted;
         }
+        if (input->ExpectTag(208)) goto parse_image_width;
+        break;
+      }
+
+      // optional int32 image_width = 26;
+      case 26: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_image_width:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &image_width_)));
+          _set_bit(18);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(216)) goto parse_image_height;
+        break;
+      }
+
+      // optional int32 image_height = 27;
+      case 27: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_image_height:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &image_height_)));
+          _set_bit(19);
+        } else {
+          goto handle_uninterpreted;
+        }
         if (input->ExpectAtEnd()) return true;
         break;
       }
@@ -1035,7 +1079,7 @@
   }
   
   // optional float time = 11;
-  if (_has_bit(18)) {
+  if (_has_bit(20)) {
     ::google::protobuf::internal::WireFormatLite::WriteFloat(11, this->time(), output);
   }
   
@@ -1065,13 +1109,13 @@
   }
   
   // optional .com.android.glesv2debugger.Message.Prop prop = 21;
-  if (_has_bit(19)) {
+  if (_has_bit(21)) {
     ::google::protobuf::internal::WireFormatLite::WriteEnum(
       21, this->prop(), output);
   }
   
   // optional float clock = 22;
-  if (_has_bit(20)) {
+  if (_has_bit(22)) {
     ::google::protobuf::internal::WireFormatLite::WriteFloat(22, this->clock(), output);
   }
   
@@ -1091,6 +1135,16 @@
     ::google::protobuf::internal::WireFormatLite::WriteInt32(25, this->pixel_type(), output);
   }
   
+  // optional int32 image_width = 26;
+  if (_has_bit(18)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(26, this->image_width(), output);
+  }
+
+  // optional int32 image_height = 27;
+  if (_has_bit(19)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(27, this->image_height(), output);
+  }
+
 }
 
 int Message::ByteSize() const {
@@ -1222,6 +1276,20 @@
           this->pixel_type());
     }
     
+    // optional int32 image_width = 26;
+    if (has_image_width()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->image_width());
+    }
+
+    // optional int32 image_height = 27;
+    if (has_image_height()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->image_height());
+    }
+
     // optional float time = 11;
     if (has_time()) {
       total_size += 1 + 4;
@@ -1312,12 +1380,18 @@
       set_pixel_type(from.pixel_type());
     }
     if (from._has_bit(18)) {
-      set_time(from.time());
+      set_image_width(from.image_width());
     }
     if (from._has_bit(19)) {
-      set_prop(from.prop());
+      set_image_height(from.image_height());
     }
     if (from._has_bit(20)) {
+      set_time(from.time());
+    }
+    if (from._has_bit(21)) {
+      set_prop(from.prop());
+    }
+    if (from._has_bit(22)) {
       set_clock(from.clock());
     }
   }
@@ -1355,6 +1429,8 @@
     std::swap(data_type_, other->data_type_);
     std::swap(pixel_format_, other->pixel_format_);
     std::swap(pixel_type_, other->pixel_type_);
+    std::swap(image_width_, other->image_width_);
+    std::swap(image_height_, other->image_height_);
     std::swap(time_, other->time_);
     std::swap(prop_, other->prop_);
     std::swap(clock_, other->clock_);
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
index b2ec5a0..4ccfebb 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
@@ -236,11 +236,13 @@
 enum Message_Type {
   Message_Type_BeforeCall = 0,
   Message_Type_AfterCall = 1,
-  Message_Type_Response = 2
+  Message_Type_AfterGeneratedCall = 2,
+  Message_Type_Response = 3,
+  Message_Type_CompleteCall = 4
 };
 bool Message_Type_IsValid(int value);
 const Message_Type Message_Type_Type_MIN = Message_Type_BeforeCall;
-const Message_Type Message_Type_Type_MAX = Message_Type_Response;
+const Message_Type Message_Type_Type_MAX = Message_Type_CompleteCall;
 const int Message_Type_Type_ARRAYSIZE = Message_Type_Type_MAX + 1;
 
 enum Message_DataType {
@@ -253,13 +255,14 @@
 const int Message_DataType_DataType_ARRAYSIZE = Message_DataType_DataType_MAX + 1;
 
 enum Message_Prop {
-  Message_Prop_Capture = 0,
+  Message_Prop_CaptureDraw = 0,
   Message_Prop_TimeMode = 1,
-  Message_Prop_ExpectResponse = 2
+  Message_Prop_ExpectResponse = 2,
+  Message_Prop_CaptureSwap = 3
 };
 bool Message_Prop_IsValid(int value);
-const Message_Prop Message_Prop_Prop_MIN = Message_Prop_Capture;
-const Message_Prop Message_Prop_Prop_MAX = Message_Prop_ExpectResponse;
+const Message_Prop Message_Prop_Prop_MIN = Message_Prop_CaptureDraw;
+const Message_Prop Message_Prop_Prop_MAX = Message_Prop_CaptureSwap;
 const int Message_Prop_Prop_ARRAYSIZE = Message_Prop_Prop_MAX + 1;
 
 // ===================================================================
@@ -510,7 +513,9 @@
   typedef Message_Type Type;
   static const Type BeforeCall = Message_Type_BeforeCall;
   static const Type AfterCall = Message_Type_AfterCall;
+  static const Type AfterGeneratedCall = Message_Type_AfterGeneratedCall;
   static const Type Response = Message_Type_Response;
+  static const Type CompleteCall = Message_Type_CompleteCall;
   static inline bool Type_IsValid(int value) {
     return Message_Type_IsValid(value);
   }
@@ -535,9 +540,10 @@
     Message_DataType_DataType_ARRAYSIZE;
   
   typedef Message_Prop Prop;
-  static const Prop Capture = Message_Prop_Capture;
+  static const Prop CaptureDraw = Message_Prop_CaptureDraw;
   static const Prop TimeMode = Message_Prop_TimeMode;
   static const Prop ExpectResponse = Message_Prop_ExpectResponse;
+  static const Prop CaptureSwap = Message_Prop_CaptureSwap;
   static inline bool Prop_IsValid(int value) {
     return Message_Prop_IsValid(value);
   }
@@ -679,6 +685,20 @@
   inline ::google::protobuf::int32 pixel_type() const;
   inline void set_pixel_type(::google::protobuf::int32 value);
   
+  // optional int32 image_width = 26;
+  inline bool has_image_width() const;
+  inline void clear_image_width();
+  static const int kImageWidthFieldNumber = 26;
+  inline ::google::protobuf::int32 image_width() const;
+  inline void set_image_width(::google::protobuf::int32 value);
+
+  // optional int32 image_height = 27;
+  inline bool has_image_height() const;
+  inline void clear_image_height();
+  static const int kImageHeightFieldNumber = 27;
+  inline ::google::protobuf::int32 image_height() const;
+  inline void set_image_height(::google::protobuf::int32 value);
+
   // optional float time = 11;
   inline bool has_time() const;
   inline void clear_time();
@@ -723,6 +743,8 @@
   int data_type_;
   ::google::protobuf::int32 pixel_format_;
   ::google::protobuf::int32 pixel_type_;
+  ::google::protobuf::int32 image_width_;
+  ::google::protobuf::int32 image_height_;
   float time_;
   int prop_;
   float clock_;
@@ -730,7 +752,7 @@
   friend void protobuf_AssignDesc_debugger_5fmessage_2eproto();
   friend void protobuf_ShutdownFile_debugger_5fmessage_2eproto();
   
-  ::google::protobuf::uint32 _has_bits_[(21 + 31) / 32];
+  ::google::protobuf::uint32 _has_bits_[(23 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
   inline bool _has_bit(int index) const {
@@ -1070,52 +1092,84 @@
   pixel_type_ = value;
 }
 
+// optional int32 image_width = 26;
+inline bool Message::has_image_width() const {
+  return _has_bit(18);
+}
+inline void Message::clear_image_width() {
+  image_width_ = 0;
+  _clear_bit(18);
+}
+inline ::google::protobuf::int32 Message::image_width() const {
+  return image_width_;
+}
+inline void Message::set_image_width(::google::protobuf::int32 value) {
+  _set_bit(18);
+  image_width_ = value;
+}
+
+// optional int32 image_height = 27;
+inline bool Message::has_image_height() const {
+  return _has_bit(19);
+}
+inline void Message::clear_image_height() {
+  image_height_ = 0;
+  _clear_bit(19);
+}
+inline ::google::protobuf::int32 Message::image_height() const {
+  return image_height_;
+}
+inline void Message::set_image_height(::google::protobuf::int32 value) {
+  _set_bit(19);
+  image_height_ = value;
+}
+
 // optional float time = 11;
 inline bool Message::has_time() const {
-  return _has_bit(18);
+  return _has_bit(20);
 }
 inline void Message::clear_time() {
   time_ = 0;
-  _clear_bit(18);
+  _clear_bit(20);
 }
 inline float Message::time() const {
   return time_;
 }
 inline void Message::set_time(float value) {
-  _set_bit(18);
+  _set_bit(20);
   time_ = value;
 }
 
 // optional .com.android.glesv2debugger.Message.Prop prop = 21;
 inline bool Message::has_prop() const {
-  return _has_bit(19);
+  return _has_bit(21);
 }
 inline void Message::clear_prop() {
   prop_ = 0;
-  _clear_bit(19);
+  _clear_bit(21);
 }
 inline ::com::android::glesv2debugger::Message_Prop Message::prop() const {
   return static_cast< ::com::android::glesv2debugger::Message_Prop >(prop_);
 }
 inline void Message::set_prop(::com::android::glesv2debugger::Message_Prop value) {
   GOOGLE_DCHECK(::com::android::glesv2debugger::Message_Prop_IsValid(value));
-  _set_bit(19);
+  _set_bit(21);
   prop_ = value;
 }
 
 // optional float clock = 22;
 inline bool Message::has_clock() const {
-  return _has_bit(20);
+  return _has_bit(22);
 }
 inline void Message::clear_clock() {
   clock_ = 0;
-  _clear_bit(20);
+  _clear_bit(22);
 }
 inline float Message::clock() const {
   return clock_;
 }
 inline void Message::set_clock(float value) {
-  _set_bit(20);
+  _set_bit(22);
   clock_ = value;
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/egl.cpp b/opengl/libs/GLES2_dbg/src/egl.cpp
index 3a20e21..eb28d06 100644
--- a/opengl/libs/GLES2_dbg/src/egl.cpp
+++ b/opengl/libs/GLES2_dbg/src/egl.cpp
@@ -18,6 +18,7 @@
 
 EGLBoolean Debug_eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
 {
+    DbgContext * const dbg = getDbgContextThreadSpecific();
     glesv2debugger::Message msg;
     struct : public FunctionCall {
         EGLDisplay dpy;
@@ -33,7 +34,21 @@
 
     msg.set_arg0(reinterpret_cast<int>(dpy));
     msg.set_arg1(reinterpret_cast<int>(draw));
-
+    if (dbg->captureSwap > 0) {
+        dbg->captureSwap--;
+        int viewport[4] = {};
+        dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
+        void * pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
+                        dbg->readBytesPerPixel);
+        dbg->hooks->gl.glReadPixels(viewport[0], viewport[1], viewport[2],
+                                    viewport[3], dbg->readFormat, dbg->readType, pixels);
+        dbg->CompressReadPixelBuffer(msg.mutable_data());
+        msg.set_data_type(msg.ReferencedImage);
+        msg.set_pixel_format(dbg->readFormat);
+        msg.set_pixel_type(dbg->readType);
+        msg.set_image_width(viewport[2]);
+        msg.set_image_height(viewport[3]);
+    }
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_eglSwapBuffers);
     return static_cast<EGLBoolean>(reinterpret_cast<int>(ret));
 }
diff --git a/opengl/libs/GLES2_dbg/src/header.h b/opengl/libs/GLES2_dbg/src/header.h
index 9218da5..c9e6c41 100644
--- a/opengl/libs/GLES2_dbg/src/header.h
+++ b/opengl/libs/GLES2_dbg/src/header.h
@@ -14,6 +14,9 @@
  ** limitations under the License.
  */
 
+#ifndef ANDROID_GLES2_DBG_HEADER_H
+#define ANDROID_GLES2_DBG_HEADER_H
+
 #include <stdlib.h>
 #include <ctype.h>
 #include <string.h>
@@ -24,9 +27,7 @@
 
 #include <cutils/log.h>
 #include <utils/Timers.h>
-#include <../../../libcore/include/StaticAssert.h>
 
-#define EGL_TRACE 1
 #include "hooks.h"
 
 #include "glesv2dbg.h"
@@ -39,8 +40,6 @@
 using namespace android;
 using namespace com::android;
 
-#define API_ENTRY(_api) Debug_##_api
-
 #ifndef __location__
 #define __HIERALLOC_STRING_0__(s)   #s
 #define __HIERALLOC_STRING_1__(s)   __HIERALLOC_STRING_0__(s)
@@ -76,7 +75,7 @@
 struct DbgContext {
 private:
     static const unsigned int LZF_CHUNK_SIZE = 256 * 1024;
-    char * lzf_buf; // malloc / free; for lzf chunk compression
+    char * lzf_buf; // malloc / free; for lzf chunk compression and other uses
 
     // used as buffer and reference frame for ReadPixels; malloc/free
     unsigned * lzf_ref [2];
@@ -84,9 +83,14 @@
     unsigned lzf_refSize, lzf_refBufSize; // bytes
 
 public:
-    const unsigned version; // 0 is GLES1, 1 is GLES2
+    const unsigned int version; // 0 is GLES1, 1 is GLES2
     const gl_hooks_t * const hooks;
-    const unsigned MAX_VERTEX_ATTRIBS;
+    const unsigned int MAX_VERTEX_ATTRIBS;
+    const GLenum readFormat, readType; // implementation supported glReadPixels
+    const unsigned int readBytesPerPixel;
+
+    unsigned int captureSwap; // number of eglSwapBuffers to glReadPixels
+    unsigned int captureDraw; // number of glDrawArrays/Elements to glReadPixels
 
     GLFunctionBitfield expectResponse;
 
@@ -119,7 +123,8 @@
     unsigned maxAttrib; // number of slots used by program
 
     DbgContext(const unsigned version, const gl_hooks_t * const hooks,
-               const unsigned MAX_VERTEX_ATTRIBS);
+               const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat,
+               const GLenum readType);
     ~DbgContext();
 
     void Fetch(const unsigned index, std::string * const data) const;
@@ -129,6 +134,8 @@
         return ptr == lzf_ref[lzf_readIndex];
     }
     void CompressReadPixelBuffer(std::string * const outStr);
+    char * GetBuffer(); // allocates lzf_buf if NULL
+    unsigned int GetBufferSize(); // allocates lzf_buf if NULL
 
     void glUseProgram(GLuint program);
     void glEnableVertexAttribArray(GLuint index);
@@ -141,9 +148,7 @@
     void glDeleteBuffers(GLsizei n, const GLuint *buffers);
 };
 
-
 DbgContext * getDbgContextThreadSpecific();
-#define DBGCONTEXT(ctx) DbgContext * const ctx = getDbgContextThreadSpecific();
 
 struct FunctionCall {
     virtual const int * operator()(gl_hooks_t::gl_t const * const _c,
@@ -152,7 +157,6 @@
 };
 
 // move these into DbgContext as static
-extern bool capture;
 extern int timeMode; // SYSTEM_TIME_
 
 extern int clientSock, serverSock;
@@ -169,3 +173,5 @@
 const int * GenerateCall(DbgContext * const dbg, const glesv2debugger::Message & cmd,
                          glesv2debugger::Message & msg, const int * const prevRet);
 }; // namespace android {
+
+#endif // #ifndef ANDROID_GLES2_DBG_HEADER_H
diff --git a/opengl/libs/GLES2_dbg/src/server.cpp b/opengl/libs/GLES2_dbg/src/server.cpp
index 7039c84..f13d6cc 100644
--- a/opengl/libs/GLES2_dbg/src/server.cpp
+++ b/opengl/libs/GLES2_dbg/src/server.cpp
@@ -28,7 +28,8 @@
 {
 
 int serverSock = -1, clientSock = -1;
-
+FILE * file = NULL;
+unsigned int MAX_FILE_SIZE = 0;
 int timeMode = SYSTEM_TIME_THREAD;
 
 static void Die(const char * msg)
@@ -38,18 +39,25 @@
     exit(1);
 }
 
-void StartDebugServer(unsigned short port)
+void StartDebugServer(const unsigned short port, const bool forceUseFile,
+                      const unsigned int maxFileSize, const char * const filePath)
 {
+    MAX_FILE_SIZE = maxFileSize;
+
     LOGD("GLESv2_dbg: StartDebugServer");
-    if (serverSock >= 0)
+    if (serverSock >= 0 || file)
         return;
 
     LOGD("GLESv2_dbg: StartDebugServer create socket");
     struct sockaddr_in server = {}, client = {};
 
     /* Create the TCP socket */
-    if ((serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
-        Die("Failed to create socket");
+    if (forceUseFile || (serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+        file = fopen(filePath, "wb");
+        if (!file)
+            Die("Failed to create socket and file");
+        else
+            return;
     }
     /* Construct the server sockaddr_in structure */
     server.sin_family = AF_INET;                  /* Internet/IP */
@@ -92,13 +100,17 @@
         close(serverSock);
         serverSock = -1;
     }
-
+    if (file) {
+        fclose(file);
+        file = NULL;
+    }
 }
 
 void Receive(glesv2debugger::Message & cmd)
 {
+    if (clientSock < 0)
+        return;
     unsigned len = 0;
-
     int received = recv(clientSock, &len, 4, MSG_WAITALL);
     if (received < 0)
         Die("Failed to receive response length");
@@ -106,7 +118,6 @@
         LOGD("received %dB: %.8X", received, len);
         Die("Received length mismatch, expected 4");
     }
-    len = ntohl(len);
     static void * buffer = NULL;
     static unsigned bufferSize = 0;
     if (bufferSize < len) {
@@ -125,6 +136,8 @@
 
 bool TryReceive(glesv2debugger::Message & cmd)
 {
+    if (clientSock < 0)
+        return false;
     fd_set readSet;
     FD_ZERO(&readSet);
     FD_SET(clientSock, &readSet);
@@ -153,7 +166,19 @@
         assert(msg.has_context_id() && msg.context_id() != 0);
     static std::string str;
     msg.SerializeToString(&str);
-    uint32_t len = htonl(str.length());
+    const uint32_t len = str.length();
+    if (clientSock < 0) {
+        if (file) {
+            fwrite(&len, sizeof(len), 1, file);
+            fwrite(str.data(), len, 1, file);
+            if (ftell(file) >= MAX_FILE_SIZE) {
+                fclose(file);
+                Die("MAX_FILE_SIZE reached");
+            }
+        }
+        pthread_mutex_unlock(&mutex);
+        return 0;
+    }
     int sent = -1;
     sent = send(clientSock, &len, sizeof(len), 0);
     if (sent != sizeof(len)) {
@@ -169,14 +194,13 @@
     }
 
     // try to receive commands even though not expecting response,
-    // since client can send SETPROP commands anytime
+    //  since client can send SETPROP and other commands anytime
     if (!msg.expect_response()) {
         if (TryReceive(cmd)) {
-            LOGD("Send: TryReceived");
             if (glesv2debugger::Message_Function_SETPROP == cmd.function())
-                LOGD("Send: received SETPROP");
+                LOGD("Send: TryReceived SETPROP");
             else
-                LOGD("Send: received something else");
+                LOGD("Send: TryReceived %u", cmd.function());
         }
     } else
         Receive(cmd);
@@ -188,9 +212,9 @@
 void SetProp(DbgContext * const dbg, const glesv2debugger::Message & cmd)
 {
     switch (cmd.prop()) {
-    case glesv2debugger::Message_Prop_Capture:
-        LOGD("SetProp Message_Prop_Capture %d", cmd.arg0());
-        capture = cmd.arg0();
+    case glesv2debugger::Message_Prop_CaptureDraw:
+        LOGD("SetProp Message_Prop_CaptureDraw %d", cmd.arg0());
+        dbg->captureDraw = cmd.arg0();
         break;
     case glesv2debugger::Message_Prop_TimeMode:
         LOGD("SetProp Message_Prop_TimeMode %d", cmd.arg0());
@@ -200,6 +224,10 @@
         LOGD("SetProp Message_Prop_ExpectResponse %d=%d", cmd.arg0(), cmd.arg1());
         dbg->expectResponse.Bit((glesv2debugger::Message_Function)cmd.arg0(), cmd.arg1());
         break;
+    case glesv2debugger::Message_Prop_CaptureSwap:
+        LOGD("SetProp CaptureSwap %d", cmd.arg0());
+        dbg->captureSwap = cmd.arg0();
+        break;
     default:
         assert(0);
     }
@@ -213,12 +241,16 @@
     glesv2debugger::Message cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(function);
+    bool expectResponse = dbg->expectResponse.Bit(function);
     msg.set_expect_response(expectResponse);
     msg.set_function(function);
-    if (!expectResponse)
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+
+    // when not exectResponse, set cmd to CONTINUE then SKIP
+    cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    cmd.set_expect_response(false);
+    glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
+    expectResponse = cmd.expect_response();
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -233,22 +265,34 @@
             msg.set_function(function);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+                cmd.set_expect_response(false);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         case glesv2debugger::Message_Function_SKIP:
             return const_cast<int *>(ret);
         case glesv2debugger::Message_Function_SETPROP:
             SetProp(dbg, cmd);
-            Receive(cmd);
+            expectResponse = cmd.expect_response();
+            if (!expectResponse) // SETPROP is "out of band"
+                cmd.set_function(oldCmd);
+            else
+                Receive(cmd);
             break;
         default:
             ret = GenerateCall(dbg, cmd, msg, ret);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(cmd.SKIP);
+                cmd.set_expect_response(expectResponse);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         }
     }
diff --git a/opengl/libs/GLES2_dbg/src/vertex.cpp b/opengl/libs/GLES2_dbg/src/vertex.cpp
index 471e5ad..7edc050 100644
--- a/opengl/libs/GLES2_dbg/src/vertex.cpp
+++ b/opengl/libs/GLES2_dbg/src/vertex.cpp
@@ -21,74 +21,13 @@
 bool capture; // capture after each glDraw*
 }
 
-void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)
-{
-    DbgContext * const dbg = getDbgContextThreadSpecific();
-    glesv2debugger::Message msg, cmd;
-    msg.set_context_id(reinterpret_cast<int>(dbg));
-    msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glReadPixels);
-    msg.set_expect_response(expectResponse);
-    msg.set_function(glesv2debugger::Message_Function_glReadPixels);
-    msg.set_arg0(x);
-    msg.set_arg1(y);
-    msg.set_arg2(width);
-    msg.set_arg3(height);
-    msg.set_arg4(format);
-    msg.set_arg5(type);
-    msg.set_arg6(reinterpret_cast<int>(pixels));
-
-    const unsigned size = width * height * GetBytesPerPixel(format, type);
-    if (!expectResponse)
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
-    Send(msg, cmd);
-    float t = 0;
-    while (true) {
-        msg.Clear();
-        nsecs_t c0 = systemTime(timeMode);
-        switch (cmd.function()) {
-        case glesv2debugger::Message_Function_CONTINUE:
-            dbg->hooks->gl.glReadPixels(x, y, width, height, format, type, pixels);
-            msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
-            msg.set_context_id(reinterpret_cast<int>(dbg));
-            msg.set_function(glesv2debugger::Message_Function_glReadPixels);
-            msg.set_type(glesv2debugger::Message_Type_AfterCall);
-            msg.set_expect_response(expectResponse);
-            if (dbg->IsReadPixelBuffer(pixels)) {
-                dbg->CompressReadPixelBuffer(msg.mutable_data());
-                msg.set_data_type(msg.ReferencedImage);
-            } else {
-                dbg->Compress(pixels, size, msg.mutable_data());
-                msg.set_data_type(msg.NonreferencedImage);
-            }
-            if (!expectResponse)
-                cmd.set_function(glesv2debugger::Message_Function_SKIP);
-            Send(msg, cmd);
-            break;
-        case glesv2debugger::Message_Function_SKIP:
-            return;
-        case glesv2debugger::Message_Function_SETPROP:
-            SetProp(dbg, cmd);
-            Receive(cmd);
-            break;
-        default:
-            GenerateCall(dbg, cmd, msg, NULL);
-            msg.set_expect_response(expectResponse);
-            if (!expectResponse)
-                cmd.set_function(cmd.SKIP);
-            Send(msg, cmd);
-            break;
-        }
-    }
-}
-
 void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
 {
     DbgContext * const dbg = getDbgContextThreadSpecific();
     glesv2debugger::Message msg, cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawArrays);
+    bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawArrays);
     msg.set_expect_response(expectResponse);
     msg.set_function(glesv2debugger::Message_Function_glDrawArrays);
     msg.set_arg0(mode);
@@ -103,11 +42,14 @@
     }
 
     void * pixels = NULL;
-    GLint readFormat = 0, readType = 0;
     int viewport[4] = {};
-    if (!expectResponse)
+    if (!expectResponse) {
         cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+        cmd.set_expect_response(false);
+    }
+    glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
+    expectResponse = cmd.expect_response();
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -121,31 +63,47 @@
             msg.set_expect_response(expectResponse);
             if (!expectResponse)
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+            if (!expectResponse) {
+                cmd.set_function(glesv2debugger::Message_Function_SKIP);
+                cmd.set_expect_response(false);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
-            if (capture) {
+            expectResponse = cmd.expect_response();
+            // TODO: pack glReadPixels data with vertex data instead of
+            //  relying on sperate call for transport, this would allow
+            //  auto generated message loop using EXTEND_Debug macro
+            if (dbg->captureDraw > 0) {
+                dbg->captureDraw--;
                 dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
 //                LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
 //                     viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
                 pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
-                                                  GetBytesPerPixel(readFormat, readType));
+                                                  dbg->readBytesPerPixel);
                 Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                                   readFormat, readType, pixels);
+                                   dbg->readFormat, dbg->readType, pixels);
             }
             break;
         case glesv2debugger::Message_Function_SKIP:
             return;
         case glesv2debugger::Message_Function_SETPROP:
             SetProp(dbg, cmd);
-            Receive(cmd);
+            expectResponse = cmd.expect_response();
+            if (!expectResponse) // SETPROP is "out of band"
+                cmd.set_function(oldCmd);
+            else
+                Receive(cmd);
             break;
         default:
             GenerateCall(dbg, cmd, msg, NULL);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(cmd.SKIP);
+                cmd.set_expect_response(expectResponse);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         }
     }
@@ -169,7 +127,7 @@
     glesv2debugger::Message msg, cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawElements);
+    bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawElements);
     msg.set_expect_response(expectResponse);
     msg.set_function(glesv2debugger::Message_Function_glDrawElements);
     msg.set_arg0(mode);
@@ -195,11 +153,14 @@
         assert(0);
 
     void * pixels = NULL;
-    GLint readFormat = 0, readType = 0;
     int viewport[4] = {};
-    if (!expectResponse)
+    if (!expectResponse) {
         cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+        cmd.set_expect_response(false);
+    }
+    glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
+    expectResponse = cmd.expect_response();
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -213,31 +174,45 @@
             msg.set_expect_response(expectResponse);
             if (!expectResponse)
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+            if (!expectResponse) {
+                cmd.set_function(glesv2debugger::Message_Function_SKIP);
+                cmd.set_expect_response(false);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
-            if (capture) {
+            expectResponse = cmd.expect_response();
+            // TODO: pack glReadPixels data with vertex data instead of
+            //  relying on sperate call for transport, this would allow
+            //  auto generated message loop using EXTEND_Debug macro
+            if (dbg->captureDraw > 0) {
+                dbg->captureDraw--;
                 dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
-//                LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
-//                     viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
                 pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
-                                                  GetBytesPerPixel(readFormat, readType));
+                                                  dbg->readBytesPerPixel);
                 Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                                   readFormat, readType, pixels);
+                                   dbg->readFormat, dbg->readType, pixels);
             }
             break;
         case glesv2debugger::Message_Function_SKIP:
             return;
         case glesv2debugger::Message_Function_SETPROP:
             SetProp(dbg, cmd);
-            Receive(cmd);
+            expectResponse = cmd.expect_response();
+            if (!expectResponse) // SETPROP is "out of band"
+                cmd.set_function(oldCmd);
+            else
+                Receive(cmd);
             break;
         default:
             GenerateCall(dbg, cmd, msg, NULL);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(cmd.SKIP);
+                cmd.set_expect_response(expectResponse);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         }
     }
diff --git a/opengl/libs/egl_tls.h b/opengl/libs/egl_tls.h
new file mode 100644
index 0000000..087989a
--- /dev/null
+++ b/opengl/libs/egl_tls.h
@@ -0,0 +1,40 @@
+/*
+ ** 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.
+ */
+
+#ifndef ANDROID_EGL_TLS_H
+#define ANDROID_EGL_TLS_H
+
+#include <EGL/egl.h>
+
+#include "glesv2dbg.h"
+
+namespace android
+{
+struct tls_t {
+    tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE), dbg(0) { }
+    ~tls_t() {
+        if (dbg)
+            DestroyDbgContext(dbg);
+    }
+
+    EGLint      error;
+    EGLContext  ctx;
+    EGLBoolean  logCallWithNoContext;
+    DbgContext* dbg;
+};
+}
+
+#endif
diff --git a/opengl/libs/glesv2dbg.h b/opengl/libs/glesv2dbg.h
index 8029dce..ee2c011 100644
--- a/opengl/libs/glesv2dbg.h
+++ b/opengl/libs/glesv2dbg.h
@@ -13,20 +13,27 @@
  ** See the License for the specific language governing permissions and
  ** limitations under the License.
  */
- 
+
 #ifndef _GLESV2_DBG_H_
 #define _GLESV2_DBG_H_
 
+#include <pthread.h>
+
 namespace android
 {
-    struct DbgContext;
-    
-    DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks);
-    void DestroyDbgContext(DbgContext * const dbg);
-    
-    void StartDebugServer(unsigned short port); // create and bind socket if haven't already
-    void StopDebugServer(); // close socket if open
-    
+struct DbgContext;
+
+DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey,
+                              const unsigned version, const gl_hooks_t * const hooks);
+
+void DestroyDbgContext(DbgContext * const dbg);
+
+// create and bind socket if haven't already, if failed to create socket or
+//  forceUseFile, then open /data/local/tmp/dump.gles2dbg, exit when size reached
+void StartDebugServer(const unsigned short port, const bool forceUseFile,
+                      const unsigned int maxFileSize, const char * const filePath);
+void StopDebugServer(); // close socket if open
+
 }; // namespace android
 
 #endif // #ifndef _GLESV2_DBG_H_