emulator: opengl: 'large' buffer optimization

This patch modifies the guest encoding libraries to avoid
un-necessary copies when sending large buffers (e.g. pixels)
to the host. Instead, the data is sent directly through a
new IOStream method (writeFully()).

On my machine, this improves the NenaMark2 benchmark
(from 50.8 to 57.1 fps). More importantly, this speeds up
the display of non-GL surfaces too, which are sent through
the special rcUpdateColorBuffer() function in gralloc_goldfish.

This is noticeable in many parts of the UI (e.g. when scrolling
through lists).

To tag a given parameter, use the new 'isLarge' variable flag
in the protocol .attrib file.

Implemented for the following encoding functions:

  rcUpdateColorBuffer
  glTexSubImage2D
  glTexImage2Di
  glBufferData
  glBufferSubData
  glCompressedTexImage2D
  glCompressedTexSubImage2D
  glTexImage3DOES
  glTexSubImage3DOES
  glCompressedTexImage3DOES
  glCompressedTexSubImage3DOES

+ Optimize the auto-generated encoder functions to avoid
  repeated function calls (for size computations).

Change-Id: I13a02607b606c40cd05984cd2051b1f3424bc2d0
diff --git a/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h b/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h
index 41d8023..445ec17 100644
--- a/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h
+++ b/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h
@@ -34,6 +34,7 @@
     virtual int commitBuffer(size_t size) = 0;
     virtual const unsigned char *readFully( void *buf, size_t len) = 0;
     virtual const unsigned char *read( void *buf, size_t *inout_len) = 0;
+    virtual int writeFully(const void* buf, size_t len) = 0;
 
     virtual ~IOStream() {
 
@@ -82,6 +83,7 @@
         return readFully(buf, len);
     }
 
+
 private:
     unsigned char *m_buf;
     size_t m_bufsize;
diff --git a/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp b/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
index 9e95604..72b7bb3 100644
--- a/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
+++ b/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
@@ -21,6 +21,15 @@
 #include <errno.h>
 #include <sys/types.h>
 
+/* Define this to 1 to enable support for the 'isLarge' variable flag
+ * that instructs the encoder to send large data buffers by a direct
+ * write through the pipe (i.e. without copying it into a temporary
+ * buffer. This has definite performance benefits when using a QEMU Pipe.
+ *
+ * Set to 0 otherwise.
+ */
+#define WITH_LARGE_SUPPORT  1
+
 EntryPoint * ApiGen::findEntryByName(const std::string & name)
 {
     EntryPoint * entry = NULL;
@@ -338,6 +347,99 @@
     return 0;
 }
 
+// Format the byte length expression for a given variable into a user-provided buffer
+// If the variable type is not a pointer, this is simply its size as a decimal constant
+// If the variable is a pointer, this will be an expression provided by the .attrib file
+// through the 'len' attribute.
+//
+// Returns 1 if the variable is a pointer, 0 otherwise
+//
+static int getVarEncodingSizeExpression(Var&  var, EntryPoint* e, char* buff, size_t bufflen)
+{
+    int ret = 0;
+    if (!var.isPointer()) {
+        snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes());
+    } else {
+        ret = 1;
+        const char* lenExpr = var.lenExpression().c_str();
+        const char* varname = var.name().c_str();
+        if (e != NULL && lenExpr[0] == '\0') {
+            fprintf(stderr, "%s: data len is undefined for '%s'\n",
+                    e->name().c_str(), varname);
+        }
+        if (var.nullAllowed()) {
+            snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr);
+        } else {
+            snprintf(buff, bufflen, "%s", lenExpr);
+        }
+    }
+    return ret;
+}
+
+static int writeVarEncodingSize(Var& var, FILE* fp)
+{
+    int ret = 0;
+    if (!var.isPointer()) {
+        fprintf(fp, "%u", (unsigned int) var.type()->bytes());
+    } else {
+        ret = 1;
+        fprintf(fp, "__size_%s", var.name().c_str());
+    }
+    return ret;
+}
+
+
+
+static void writeVarEncodingExpression(Var& var, FILE* fp)
+{
+    const char* varname = var.name().c_str();
+
+    if (var.isPointer()) {
+        // encode a pointer header
+        fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname);
+
+        Var::PointerDir dir = var.pointerDir();
+        if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
+            if (var.nullAllowed()) {
+                fprintf(fp, "\tif (%s != NULL) ", varname);
+            } else {
+                fprintf(fp, "\t");
+            }
+
+            if (var.packExpression().size() != 0) {
+                fprintf(fp, "%s;", var.packExpression().c_str());
+            } else {
+                fprintf(fp, "memcpy(ptr, %s, __size_%s);",
+                        varname, varname);
+            }
+
+            fprintf(fp, "ptr += __size_%s;\n", varname);
+        }
+    } else {
+        // encode a non pointer variable
+        if (!var.isVoid()) {
+            fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
+                    var.type()->name().c_str(), varname,
+                    (uint) var.type()->bytes());
+        }
+    }
+}
+
+#if WITH_LARGE_SUPPORT
+static void writeVarLargeEncodingExpression(Var& var, FILE* fp)
+{
+    const char* varname = var.name().c_str();
+
+    fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname);
+    if (var.nullAllowed()) {
+        fprintf(fp, "\tif (%s != NULL) ", varname);
+    } else {
+        fprintf(fp, "\t");
+    }
+    fprintf(fp, "stream->writeFully(%s, __size_%s);\n", varname, varname);
+}
+#endif /* WITH_LARGE_SUPPORT */
+
 int ApiGen::genEncoderImpl(const std::string &filename)
 {
     FILE *fp = fopen(filename.c_str(), "wt");
@@ -368,46 +470,139 @@
         fprintf(fp, "{\n");
 
 //      fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str());
-        fprintf(fp, "\n\t%s *ctx = (%s *)self;\n\n",
+        fprintf(fp, "\n\t%s *ctx = (%s *)self;\n",
                 classname.c_str(),
                 classname.c_str());
-
-        // size calculation ;
-        fprintf(fp, "\t size_t packetSize = ");
-
+        fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n\n");
         VarsArray & evars = e->vars();
+        size_t  maxvars = evars.size();
+        size_t  j;
+
+        char    buff[256];
+
+        // Define the __size_XXX variables that contain the size of data
+        // associated with pointers.
+        for (j = 0; j < maxvars; j++) {
+            Var& var = evars[j];
+
+            if (!var.isPointer())
+                continue;
+
+            const char* varname = var.name().c_str();
+            fprintf(fp, "\tconst unsigned int __size_%s = ", varname);
+
+            getVarEncodingSizeExpression(var, e, buff, sizeof(buff));
+            fprintf(fp, "%s;\n", buff);
+        }
+
+#if WITH_LARGE_SUPPORT
+        // We need to take care of 'isLarge' variable in a special way
+        // Anything before an isLarge variable can be packed into a single
+        // buffer, which is then commited. Each isLarge variable is a pointer
+        // to data that can be written to directly through the pipe, which
+        // will be instant when using a QEMU pipe
+
+        size_t  nvars   = 0;
+        size_t  npointers = 0;
+
+        // First, compute the total size, 8 bytes for the opcode + payload size
+        fprintf(fp, "\t unsigned char *ptr;\n");
+        fprintf(fp, "\t const size_t packetSize = 8");
+
+        for (j = 0; j < maxvars; j++) {
+            fprintf(fp, " + ");
+            npointers += writeVarEncodingSize(evars[j], fp);
+        }
+        if (npointers > 0) {
+            fprintf(fp, " + %u*4", npointers);
+        }
+        fprintf(fp, ";\n");
+
+        // We need to divide the packet into fragments. Each fragment contains
+        // either copied arguments to a temporary buffer, or direct writes for
+        // large variables.
+        //
+        // The first fragment must also contain the opcode+payload_size
+        //
+        nvars = 0;
+        while (nvars < maxvars || maxvars == 0) {
+
+            // Skip over non-large fields
+            for (j = nvars; j < maxvars; j++) {
+                if (evars[j].isLarge())
+                    break;
+            }
+
+            // Write a fragment if needed.
+            if (nvars == 0 || j > nvars) {
+                const char* plus = "";
+
+                if (nvars == 0 && j == maxvars) {
+                    // Simple shortcut for the common case where we don't have large variables;
+                    fprintf(fp, "\tptr = stream->alloc(packetSize);\n");
+
+                } else {
+                    // allocate buffer from the stream until the first large variable
+                    fprintf(fp, "\tptr = stream->alloc(");
+                    plus = "";
+
+                    if (nvars == 0) {
+                        fprintf(fp,"8"); plus = " + ";
+                    }
+                    if (j > nvars) {
+                        npointers = 0;
+                        for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
+                            fprintf(fp, "%s", plus); plus = " + ";
+                            npointers += writeVarEncodingSize(evars[j], fp);
+                        }
+                        if (npointers > 0) {
+                            fprintf(fp, "%s%u*4", plus, npointers); plus = " + ";
+                        }
+                    }
+                    fprintf(fp,");\n");
+                }
+
+                // encode packet header if needed.
+                if (nvars == 0) {
+                    fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n", e->name().c_str());
+                    fprintf(fp, "\t*(unsigned int *)(ptr) = (unsigned int) packetSize; ptr += 4;\n");
+                }
+
+                if (maxvars == 0)
+                    break;
+
+                // encode non-large fields in this fragment
+                for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
+                    writeVarEncodingExpression(evars[j],fp);
+                }
+
+                // Ensure the fragment is commited if it is followed by a large variable
+                if (j < maxvars) {
+                    fprintf(fp, "\tstream->flush();\n");
+                }
+            }
+
+            // If we have one or more large variables, write them directly.
+            // As size + data
+            for ( ; j < maxvars && evars[j].isLarge(); j++) {
+                writeVarLargeEncodingExpression(evars[j], fp);
+            }
+
+            nvars = j;
+        }
+
+#else /* !WITH_LARGE_SUPPORT */
         size_t nvars = evars.size();
         size_t npointers = 0;
+        fprintf(fp, "\t const size_t packetSize = 8");
         for (size_t j = 0; j < nvars; j++) {
-            fprintf(fp, "%s ", j == 0 ? "" : " +");
-            if (evars[j].isPointer()) {
-                npointers++;
-
-                if (evars[j].lenExpression() == "") {
-                    fprintf(stderr, "%s: data len is undefined for '%s'\n",
-                            e->name().c_str(), evars[j].name().c_str());
-                }
-
-                if (evars[j].nullAllowed()) {
-                    fprintf(fp, "(%s != NULL ? %s : 0)",
-                            evars[j].name().c_str(),
-                            evars[j].lenExpression().c_str());
-                } else {
-                    if (evars[j].pointerDir() == Var::POINTER_IN ||
-                        evars[j].pointerDir() == Var::POINTER_INOUT) {
-                        fprintf(fp, "%s", evars[j].lenExpression().c_str());
-                    } else {
-                        fprintf(fp, "0");
-                    }
-                }
-            } else {
-                fprintf(fp, "%u", (unsigned int) evars[j].type()->bytes());
-            }
+            npointers += getVarEncodingSizeExpression(evars[j],e,buff,sizeof(buff));
+            fprintf(fp, " + %s", buff);
         }
-        fprintf(fp, " %s 8 + %u * 4;\n", nvars != 0 ? "+" : "", (unsigned int) npointers);
+        fprintf(fp, " + %u * 4;\n", (unsigned int) npointers);
 
         // allocate buffer from the stream;
-        fprintf(fp, "\t unsigned char *ptr = ctx->m_stream->alloc(packetSize);\n\n");
+        fprintf(fp, "\t unsigned char *ptr = stream->alloc(packetSize);\n\n");
 
         // encode into the stream;
         fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n",  e->name().c_str());
@@ -415,62 +610,23 @@
 
         // out variables
         for (size_t j = 0; j < nvars; j++) {
-            if (evars[j].isPointer()) {
-                // encode a pointer header
-                if (evars[j].nullAllowed()) {
-                    fprintf(fp, "\t*(unsigned int *)(ptr) = (%s != NULL) ? %s : 0; ptr += 4; \n",
-                            evars[j].name().c_str(), evars[j].lenExpression().c_str());
-                } else {
-                    fprintf(fp, "\t*(unsigned int *)(ptr) = %s; ptr += 4; \n",
-                            evars[j].lenExpression().c_str());
-                }
-
-                Var::PointerDir dir = evars[j].pointerDir();
-                if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
-                    if (evars[j].nullAllowed()) {
-                        fprintf(fp, "\tif (%s != NULL) ", evars[j].name().c_str());
-                    } else {
-                        fprintf(fp, "\t");
-                    }
-
-                    if (evars[j].packExpression().size() != 0) {
-                        fprintf(fp, "%s;", evars[j].packExpression().c_str());
-                    } else {
-                        fprintf(fp, "memcpy(ptr, %s, %s);",
-                                evars[j].name().c_str(),
-                                evars[j].lenExpression().c_str());
-                    }
-
-                    if (evars[j].nullAllowed()) {
-                        fprintf(fp, "ptr += %s == NULL ? 0 : %s; \n", evars[j].name().c_str(), evars[j].lenExpression().c_str());
-                    } else {
-                        fprintf(fp, "ptr += %s;\n", evars[j].lenExpression().c_str());
-                    }
-                }
-            } else {
-                // encode a non pointer variable
-                if (!evars[j].isVoid()) {
-                    fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
-                            evars[j].type()->name().c_str(), evars[j].name().c_str(),
-                            (uint) evars[j].type()->bytes());
-                }
-            }
+            writeVarEncodingExpression(evars[j], fp);
         }
+#endif /* !WITH_LARGE_SUPPORT */
+
         // in variables;
         for (size_t j = 0; j < nvars; j++) {
             if (evars[j].isPointer()) {
                 Var::PointerDir dir = evars[j].pointerDir();
                 if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) {
+                    const char* varname = evars[j].name().c_str();
                     if (evars[j].nullAllowed()) {
-                        fprintf(fp, "\tif (%s != NULL) ctx->m_stream->readback(%s, %s);\n",
-                                evars[j].name().c_str(),
-                                evars[j].name().c_str(),
-                                evars[j].lenExpression().c_str());
+                        fprintf(fp, "\tif (%s != NULL) ",varname);
                     } else {
-                        fprintf(fp, "\tctx->m_stream->readback(%s, %s);\n",
-                                evars[j].name().c_str(),
-                                evars[j].lenExpression().c_str());
+                        fprintf(fp, "\t");
                     }
+                    fprintf(fp, "stream->readback(%s, __size_%s);\n",
+                            varname, varname);
                 }
             }
         }
@@ -482,7 +638,7 @@
             fprintf(fp, "\t return NULL;\n");
         } else if (e->retval().type()->name() != "void") {
             fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str());
-            fprintf(fp, "\tctx->m_stream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes());
+            fprintf(fp, "\tstream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes());
             fprintf(fp, "\treturn retval;\n");
         }
         fprintf(fp, "}\n\n");
diff --git a/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp b/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
index 413b56a..044433d 100644
--- a/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
+++ b/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
@@ -286,22 +286,36 @@
                     (unsigned int)lc, varname.c_str(), name().c_str());
             return -2;
         }
-        pos = last;
-        std::string flag = getNextToken(line, pos, &last, WHITESPACE);
-        if (flag.size() == 0) {
-            fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc);
-            return -3;
-        }
-
-        if (flag == "nullAllowed") {
-            if (v->isPointer()) {
-                v->setNullAllowed(true);
-            } else {
-                fprintf(stderr, "WARNING: %u: setting nullAllowed for non-pointer variable %s\n",
-                        (unsigned int) lc, v->name().c_str());
+        int count = 0;
+        for (;;) {
+            pos = last;
+            std::string flag = getNextToken(line, pos, &last, WHITESPACE);
+            if (flag.size() == 0) {
+                if (count == 0) {
+                    fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc);
+                    return -3;
+                }
+                break;
             }
-        } else {
-            fprintf(stderr, "WARNING: %u: unknow flag %s\n", (unsigned int)lc, flag.c_str());
+            count++;
+
+            if (flag == "nullAllowed") {
+                if (v->isPointer()) {
+                    v->setNullAllowed(true);
+                } else {
+                    fprintf(stderr, "WARNING: %u: setting nullAllowed for non-pointer variable %s\n",
+                            (unsigned int) lc, v->name().c_str());
+                }
+            } else if (flag == "isLarge") {
+                if (v->isPointer()) {
+                    v->setIsLarge(true);
+                } else {
+                    fprintf(stderr, "WARNING: %u: setting isLarge flag for a non-pointer variable %s\n",
+                            (unsigned int) lc, v->name().c_str());
+                }
+            } else {
+                fprintf(stderr, "WARNING: %u: unknow flag %s\n", (unsigned int)lc, flag.c_str());
+            }
         }
     } else if (token == "custom_pack") {
         pos = last;
diff --git a/tools/emulator/opengl/host/tools/emugen/README b/tools/emulator/opengl/host/tools/emugen/README
index 4d2c28d..5df11a3 100644
--- a/tools/emulator/opengl/host/tools/emugen/README
+++ b/tools/emulator/opengl/host/tools/emugen/README
@@ -313,7 +313,10 @@
 
  var_flag 
  	 description : set variable flags
- 	 format: var_flag <varname> < nullAllowed | ... >
+ 	 format: var_flag <varname> < nullAllowed | isLarge | ... >
+
+        nullAllowed -> for pointer variables, indicates that NULL is a valid value
+        isLarge     -> for pointer variables, indicates that the data should be sent without an intermediate copy
 
  flag
 	description: set entry point flag; 
diff --git a/tools/emulator/opengl/host/tools/emugen/Var.h b/tools/emulator/opengl/host/tools/emugen/Var.h
index c9735c7..658b805 100644
--- a/tools/emulator/opengl/host/tools/emugen/Var.h
+++ b/tools/emulator/opengl/host/tools/emugen/Var.h
@@ -30,6 +30,7 @@
         m_lenExpression(""),
         m_pointerDir(POINTER_IN),
         m_nullAllowed(false),
+        m_isLarge(false),
         m_packExpression(""),
         m_paramCheckExpression("")
 
@@ -46,8 +47,9 @@
         m_lenExpression(lenExpression),
         m_pointerDir(dir),
         m_nullAllowed(false),
+        m_isLarge(false),
         m_packExpression(packExpression),
-		m_paramCheckExpression("")	
+	m_paramCheckExpression("")
     {
     }
 
@@ -60,6 +62,7 @@
         m_packExpression = packExpression;
         m_pointerDir = dir;
         m_nullAllowed = false;
+        m_isLarge = false;
 
     }
 
@@ -76,7 +79,9 @@
     void setPointerDir(PointerDir dir) { m_pointerDir = dir; }
     PointerDir pointerDir() { return m_pointerDir; }
     void setNullAllowed(bool state) { m_nullAllowed = state; }
+    void setIsLarge(bool state) { m_isLarge = state; }
     bool nullAllowed() const { return m_nullAllowed; }
+    bool isLarge() const { return m_isLarge; }
     void printType(FILE *fp) { fprintf(fp, "%s", m_type->name().c_str()); }
     void printTypeName(FILE *fp) { printType(fp); fprintf(fp, " %s", m_name.c_str()); }
 
@@ -87,6 +92,7 @@
     std::string m_lenExpression; // an expression to calcualte a pointer data size
     PointerDir m_pointerDir;
     bool m_nullAllowed;
+    bool m_isLarge;
     std::string m_packExpression; // an expression to pack data into the stream
     std::string m_paramCheckExpression; //an expression to check parameter value
 
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp
index 244cfbb..ddc56d0 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp
@@ -85,13 +85,18 @@
 
 int SocketStream::commitBuffer(size_t size)
 {
+    return writeFully(m_buf, size);
+}
+
+int SocketStream::writeFully(const void* buffer, size_t size)
+{
     if (!valid()) return -1;
 
     size_t res = size;
     int retval = 0;
 
     while (res > 0) {
-        ssize_t stat = ::send(m_sock, (const char *)(m_buf) + (size - res), res, 0);
+        ssize_t stat = ::send(m_sock, (const char *)buffer + (size - res), res, 0);
         if (stat < 0) {
             if (errno != EINTR) {
                 retval =  stat;
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h
index c54dea7..3a501b4 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h
@@ -37,6 +37,7 @@
 
     bool valid() { return m_sock >= 0; }
     virtual int recv(void *buf, size_t len);
+    virtual int writeFully(const void *buf, size_t len);
 
 protected:
     int            m_sock;
@@ -44,7 +45,6 @@
     unsigned char *m_buf;
 
     SocketStream(int sock, size_t bufSize);
-    int writeFully(const void *buf, size_t len);
 };
 
 #endif /* __SOCKET_STREAM_H */
diff --git a/tools/emulator/opengl/system/GLESv1_enc/gl.attrib b/tools/emulator/opengl/system/GLESv1_enc/gl.attrib
index fed0f7a..9b84f89 100644
--- a/tools/emulator/opengl/system/GLESv1_enc/gl.attrib
+++ b/tools/emulator/opengl/system/GLESv1_enc/gl.attrib
@@ -240,7 +240,7 @@
 glTexImage2D
 	dir pixels in
 	len pixels pixelDataSize(self, width, height, format, type, 0)
-	var_flag pixels nullAllowed
+	var_flag pixels nullAllowed isLarge
 
 #void glTexParameteriv(GLenum target, GLenum pname, GLint *params)
 glTexParameteriv
@@ -253,6 +253,7 @@
 #void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
 glTexSubImage2D
 	len pixels pixelDataSize(self, width, height, format, type, 0)
+        var_flag pixels isLarge
 
 #void glVertexPointer(GLint size, GLenum type, GLsizei stride, GLvoid *pointer)
 # we treat the pointer as an offset to a VBO
diff --git a/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib b/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib
index 538c453..7fe9a66 100644
--- a/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib
+++ b/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib
@@ -9,20 +9,22 @@
 #void glBufferData(GLenum target, GLsizeiptr size, GLvoid *data, GLenum usage)
 glBufferData
 	len data size
-	var_flag data nullAllowed
+	var_flag data nullAllowed isLarge
 
 #void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data)
 glBufferSubData
 	len data size
+        var_flag data isLarge
 
 #void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLvoid *data)
 glCompressedTexImage2D
 	len data imageSize
-    var_flag data nullAllowed
+    var_flag data nullAllowed isLarge
 
 #void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, GLvoid *data)
 glCompressedTexSubImage2D
 	len data imageSize
+        var_flag data isLarge
 
 #void glDeleteBuffers(GLsizei n, GLuint *buffers)
 glDeleteBuffers
@@ -243,7 +245,7 @@
 glTexImage2D
 	dir pixels in
 	len pixels pixelDataSize(self, width, height, format, type, 0)
-	var_flag pixels nullAllowed
+	var_flag pixels nullAllowed isLarge
 
 #void glTexParameterfv(GLenum target, GLenum pname, GLfloat *params)
 glTexParameterfv
@@ -255,6 +257,7 @@
 #void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
 glTexSubImage2D
 	len pixels pixelDataSize(self, width, height, format, type, 0)
+        var_flag pixels isLarge
 	
 #void glUniform1fv(GLint location, GLsizei count, GLfloat *v)
 glUniform1fv
@@ -333,20 +336,23 @@
 
 #void glTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, GLvoid *pixels)
 glTexImage3DOES
-	len pixels pixelDataSize3D(self, width, height, depth, format, type, 0)	
-	var_flag pixels nullAllowed
+	len pixels pixelDataSize3D(self, width, height, depth, format, type, 0)
+	var_flag pixels nullAllowed isLarge
 
 #void glTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *pixels)
 glTexSubImage3DOES
 	len pixels pixelDataSize3D(self, width, height, depth, format, type, 0)	
+        var_flag pixels isLarge
 
 #void glCompressedTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, GLvoid *data)
 glCompressedTexImage3DOES
 	len data imageSize
+        var_flag data isLarge
 
 #void glCompressedTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, GLvoid *data)
 glCompressedTexSubImage3DOES
 	len data imageSize
+        var_flag data isLarge
 
 #void glDeleteVertexArraysOES(GLsizei n, GLuint *arrays)
 glDeleteVertexArraysOES
diff --git a/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h b/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h
index db36286..57ee399 100644
--- a/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h
+++ b/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h
@@ -39,8 +39,7 @@
     bool valid() { return m_sock >= 0; }
     int recv(void *buf, size_t len);
 
-private:
-    int writeFully(const void *buf, size_t len);
+    virtual int writeFully(const void *buf, size_t len);
 
 private:
     int m_sock;
diff --git a/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib b/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib
index c51ae0e..8b9972f 100644
--- a/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib
+++ b/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib
@@ -26,10 +26,10 @@
 
 rcChooseConfig
     dir attribs in
-	len attribs attribs_size
-	dir configs out
+    len attribs attribs_size
+    dir configs out
     var_flag configs nullAllowed
-	len configs configs_size*sizeof(uint32_t)
+    len configs configs_size*sizeof(uint32_t)
 
 rcReadColorBuffer
     dir pixels out
@@ -38,3 +38,4 @@
 rcUpdateColorBuffer
     dir pixels in
     len pixels (((glUtilsPixelBitSize(format, type) * width) >> 3) * height)
+    var_flag pixels isLarge