Merge Android Pie into master

Bug: 112104996
Change-Id: Ia560f566f920ec33a00c74c751319acde0d7bc33
diff --git a/host/include/libOpenglRender/IOStream.h b/host/include/libOpenglRender/IOStream.h
index 445ec17..1d32ea1 100644
--- a/host/include/libOpenglRender/IOStream.h
+++ b/host/include/libOpenglRender/IOStream.h
@@ -83,6 +83,8 @@
         return readFully(buf, len);
     }
 
+    void readbackPixels(void* context, int width, int height, unsigned int format, unsigned int type, void* pixels);
+
 
 private:
     unsigned char *m_buf;
diff --git a/shared/OpenglCodecCommon/GLClientState.cpp b/shared/OpenglCodecCommon/GLClientState.cpp
index 6e9c8f6..7d40389 100644
--- a/shared/OpenglCodecCommon/GLClientState.cpp
+++ b/shared/OpenglCodecCommon/GLClientState.cpp
@@ -664,6 +664,29 @@
     return 1;
 }
 
+void GLClientState::getPackingOffsets2D(GLsizei width, GLsizei height, GLenum format, GLenum type, int* startOffset, int* pixelRowSize, int* totalRowSize, int* skipRows) const
+{
+    if (width <= 0 || height <= 0) {
+        *startOffset = 0;
+        *pixelRowSize = 0;
+        *totalRowSize = 0;
+        return;
+    }
+
+    GLESTextureUtils::computePackingOffsets2D(
+            width, height,
+            format, type,
+            m_pixelStore.pack_alignment,
+            m_pixelStore.pack_row_length,
+            m_pixelStore.pack_skip_pixels,
+            m_pixelStore.pack_skip_rows,
+            startOffset,
+            pixelRowSize,
+            totalRowSize);
+
+    *skipRows = m_pixelStore.pack_skip_rows;
+}
+
 void GLClientState::setNumActiveUniformsInUniformBlock(GLuint program, GLuint uniformBlockIndex, GLint numActiveUniforms) {
     UniformBlockInfoKey key;
     key.program = program;
diff --git a/shared/OpenglCodecCommon/GLClientState.h b/shared/OpenglCodecCommon/GLClientState.h
index e41cf44..675cea4 100644
--- a/shared/OpenglCodecCommon/GLClientState.h
+++ b/shared/OpenglCodecCommon/GLClientState.h
@@ -239,6 +239,7 @@
     size_t pixelDataSize(GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, int pack) const;
     size_t pboNeededDataSize(GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, int pack) const;
     size_t clearBufferNumElts(GLenum buffer) const;
+    void getPackingOffsets2D(GLsizei width, GLsizei height, GLenum format, GLenum type, int* startOffset, int* pixelRowSize, int* totalRowSize, int* skipRows) const;
 
     void setCurrentProgram(GLint program) { m_currentProgram = program; }
     void setCurrentShaderProgram(GLint program) { m_currentShaderProgram = program; }
diff --git a/shared/OpenglCodecCommon/GLESTextureUtils.cpp b/shared/OpenglCodecCommon/GLESTextureUtils.cpp
index 1aef8cb..4572905 100644
--- a/shared/OpenglCodecCommon/GLESTextureUtils.cpp
+++ b/shared/OpenglCodecCommon/GLESTextureUtils.cpp
@@ -284,4 +284,29 @@
     return end - start;
 }
 
+void computePackingOffsets2D(
+        GLsizei width, GLsizei height,
+        GLenum format, GLenum type,
+        int packAlignment,
+        int packRowLength,
+        int packSkipPixels,
+        int packSkipRows,
+        int* startOffset,
+        int* packingPixelRowSize,
+        int* packingTotalRowSize) {
+
+    int widthTotal = (packRowLength == 0) ? width : packRowLength;
+    int totalRowSize = computePitch(widthTotal, format, type, packAlignment);
+    int pixelsOnlyRowSize = computePitch(width, format, type, packAlignment);
+
+    int packingOffsetStart =
+        computePackingOffset(
+                format, type, widthTotal, height, packAlignment, packSkipPixels, packSkipRows, 0 /* skip images = 0 */);
+
+    if (startOffset) *startOffset = packingOffsetStart;
+    if (packingPixelRowSize) *packingPixelRowSize = pixelsOnlyRowSize;
+    if (packingTotalRowSize) *packingTotalRowSize = totalRowSize;
+}
+
+
 } // namespace GLESTextureUtils
diff --git a/shared/OpenglCodecCommon/GLESTextureUtils.h b/shared/OpenglCodecCommon/GLESTextureUtils.h
index 906e590..f623d23 100644
--- a/shared/OpenglCodecCommon/GLESTextureUtils.h
+++ b/shared/OpenglCodecCommon/GLESTextureUtils.h
@@ -37,6 +37,21 @@
         int unpackSkipRows,
         int unpackSkipImages);
 
+// Writes out |height| offsets for glReadPixels to read back
+// data in separate rows of pixels. Returns:
+// 1. |startOffset|: offset in bytes to apply at the beginning
+// 2. |packingPixelRowSize|: the buffer size in bytes that has the actual pixels per row.
+// 2. |packingTotalRowSize|: the length in bytes of each row including the padding from row length.
+void computePackingOffsets2D(
+        GLsizei width, GLsizei height,
+        GLenum format, GLenum type,
+        int packAlignment,
+        int packRowLength,
+        int packSkipPixels,
+        int packSkipRows,
+        int* startOffset,
+        int* packingPixelRowSize,
+        int* packingTotalRowSize);
 
 } // namespace GLESTextureUtils
 #endif
diff --git a/shared/OpenglCodecCommon/GLSharedGroup.cpp b/shared/OpenglCodecCommon/GLSharedGroup.cpp
index 9da011a..19dfe0f 100755
--- a/shared/OpenglCodecCommon/GLSharedGroup.cpp
+++ b/shared/OpenglCodecCommon/GLSharedGroup.cpp
@@ -180,7 +180,7 @@
     for (GLuint i = 0; i < m_numIndexes; i++) {
         GLint elemIndex = appLoc - m_Indexes[i].appBase;
         if (elemIndex >= 0 && elemIndex < m_Indexes[i].size) {
-            if (m_Indexes[i].type == GL_TEXTURE_2D) {
+            if (m_Indexes[i].type == GL_SAMPLER_2D) {
                 m_Indexes[i].samplerValue = val;
                 if (target) {
                     if (m_Indexes[i].flags & INDEX_FLAG_SAMPLER_EXTERNAL) {
diff --git a/shared/OpenglCodecCommon/glUtils.cpp b/shared/OpenglCodecCommon/glUtils.cpp
index 7e5e4a2..3136d13 100644
--- a/shared/OpenglCodecCommon/glUtils.cpp
+++ b/shared/OpenglCodecCommon/glUtils.cpp
@@ -125,6 +125,8 @@
     case GL_DEPTH_FUNC:
     case GL_DEPTH_BITS:
     case GL_MAX_CLIP_PLANES:
+    case GL_MAX_COLOR_ATTACHMENTS:
+    case GL_MAX_DRAW_BUFFERS:
     case GL_GREEN_BITS:
     case GL_MAX_MODELVIEW_STACK_DEPTH:
     case GL_MAX_PROJECTION_STACK_DEPTH:
@@ -243,14 +245,18 @@
     case GL_STENCIL_BACK_PASS_DEPTH_PASS:
     case GL_STENCIL_BACK_WRITEMASK:
     case GL_TEXTURE_2D:
+    case GL_TEXTURE_BASE_LEVEL:
     case GL_TEXTURE_BINDING_2D:
     case GL_TEXTURE_BINDING_CUBE_MAP:
     case GL_TEXTURE_BINDING_EXTERNAL_OES:
+    case GL_TEXTURE_COMPARE_FUNC:
+    case GL_TEXTURE_COMPARE_MODE:
     case GL_TEXTURE_COORD_ARRAY:
     case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING:
     case GL_TEXTURE_COORD_ARRAY_SIZE:
     case GL_TEXTURE_COORD_ARRAY_STRIDE:
     case GL_TEXTURE_COORD_ARRAY_TYPE:
+    case GL_TEXTURE_IMMUTABLE_FORMAT:
     case GL_UNPACK_ALIGNMENT:
     case GL_VERTEX_ARRAY:
     case GL_VERTEX_ARRAY_BUFFER_BINDING:
@@ -260,8 +266,15 @@
     case GL_SPOT_CUTOFF:
     case GL_TEXTURE_MIN_FILTER:
     case GL_TEXTURE_MAG_FILTER:
+    case GL_TEXTURE_MAX_LOD:
+    case GL_TEXTURE_MIN_LOD:
     case GL_TEXTURE_WRAP_S:
     case GL_TEXTURE_WRAP_T:
+    case GL_TEXTURE_WRAP_R:
+    case GL_TEXTURE_SWIZZLE_R:
+    case GL_TEXTURE_SWIZZLE_G:
+    case GL_TEXTURE_SWIZZLE_B:
+    case GL_TEXTURE_SWIZZLE_A:
     case GL_GENERATE_MIPMAP:
     case GL_GENERATE_MIPMAP_HINT:
     case GL_RENDERBUFFER_WIDTH_OES:
@@ -303,6 +316,8 @@
     case GL_SHADER_SOURCE_LENGTH:
     case GL_CURRENT_PROGRAM:
     case GL_SUBPIXEL_BITS:
+    case GL_MAX_3D_TEXTURE_SIZE:
+    case GL_MAX_ARRAY_TEXTURE_LAYERS:
     case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
     case GL_NUM_SHADER_BINARY_FORMATS:
     case GL_SHADER_COMPILER:
@@ -327,10 +342,13 @@
     case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
     case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
     case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
+    case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
     case GL_CURRENT_QUERY:
     case GL_QUERY_RESULT:
     case GL_QUERY_RESULT_AVAILABLE:
     case GL_READ_BUFFER:
+    case GL_NUM_PROGRAM_BINARY_FORMATS:
+    case GL_PROGRAM_BINARY_FORMATS:
 
     case GL_ACTIVE_ATOMIC_COUNTER_BUFFERS:
     case GL_ACTIVE_ATTRIBUTES:
@@ -343,6 +361,7 @@
     case GL_DELETE_STATUS:
     case GL_INFO_LOG_LENGTH:
     case GL_LINK_STATUS:
+    case GL_PROGRAM_BINARY_LENGTH:
     case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
     case GL_PROGRAM_SEPARABLE:
     case GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
@@ -444,6 +463,10 @@
     }
 }
 
+#ifndef GL_RGBA16F
+#define GL_RGBA16F                        0x881A
+#endif // GL_RGBA16F
+
 int glUtilsPixelBitSize(GLenum format, GLenum type)
 {
     int components = 0;
@@ -455,7 +478,16 @@
         componentsize = 8;
         break;
     case GL_SHORT:
+    case GL_HALF_FLOAT:
     case GL_UNSIGNED_SHORT:
+        componentsize = 16;
+        break;
+    case GL_INT:
+    case GL_UNSIGNED_INT:
+    case GL_FLOAT:
+    case GL_FIXED:
+        componentsize = 32;
+        break;
     case GL_UNSIGNED_SHORT_5_6_5:
     case GL_UNSIGNED_SHORT_4_4_4_4:
     case GL_UNSIGNED_SHORT_5_5_5_1:
@@ -464,15 +496,12 @@
     case GL_RGBA4_OES:
         pixelsize = 16;
         break;
-    case GL_INT:
-    case GL_UNSIGNED_INT:
-    case GL_FLOAT:
-    case GL_FIXED:
+    case GL_UNSIGNED_INT_2_10_10_10_REV:
     case GL_UNSIGNED_INT_24_8_OES:
         pixelsize = 32;
         break;
     default:
-        ERR("glUtilsPixelBitSize: unknown pixel type - assuming pixel data 0\n");
+        ERR("glUtilsPixelBitSize: unknown pixel type %d - assuming pixel data 0\n", type);
         componentsize = 0;
     }
 
@@ -502,11 +531,16 @@
         case GL_BGRA_EXT:
             components = 4;
             break;
+        case GL_RGBA16F:
+            pixelsize = 64;
+            break;
         default:
-            ERR("glUtilsPixelBitSize: unknown pixel format...\n");
+            ERR("glUtilsPixelBitSize: unknown pixel format %d\n", format);
             components = 0;
         }
-        pixelsize = components * componentsize;
+        if (pixelsize == 0) {
+            pixelsize = components * componentsize;
+        }
     }
 
     return pixelsize;
diff --git a/system/GLESv1/gl.cpp b/system/GLESv1/gl.cpp
index e20b45d..6e07004 100644
--- a/system/GLESv1/gl.cpp
+++ b/system/GLESv1/gl.cpp
@@ -86,28 +86,32 @@
     }
 }
 
-void glEGLImageTargetRenderbufferStorageOES(void *self, GLenum target, GLeglImageOES image)
+void glEGLImageTargetRenderbufferStorageOES(void *self, GLenum target, GLeglImageOES img)
 {
     (void)self;
     (void)target;
 
-    DBG("glEGLImageTargetRenderbufferStorageOES v1 target=%#x image=%p",
-            target, image);
+    DBG("glEGLImageTargetRenderbufferStorageOES v1 image=%p\n", img);
     //TODO: check error - we don't have a way to set gl error
-    android_native_buffer_t* native_buffer = (android_native_buffer_t*)image;
+    EGLImage_t *image = (EGLImage_t*)img;
 
-    if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
-        return;
+    if (image->target == EGL_NATIVE_BUFFER_ANDROID) {
+        android_native_buffer_t* native_buffer = ((EGLImage_t*)image)->native_buffer;
+
+        if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
+            return;
+        }
+
+        if (native_buffer->common.version != sizeof(android_native_buffer_t)) {
+            return;
+        }
+
+        DEFINE_AND_VALIDATE_HOST_CONNECTION();
+        rcEnc->rcBindRenderbuffer(rcEnc, ((cb_handle_t *)(native_buffer->handle))->hostHandle);
+    } else {
+        //TODO
     }
 
-    if (native_buffer->common.version != sizeof(android_native_buffer_t)) {
-        return;
-    }
-
-    DEFINE_AND_VALIDATE_HOST_CONNECTION();
-    rcEnc->rcBindRenderbuffer(rcEnc,
-            ((cb_handle_t *)(native_buffer->handle))->hostHandle);
-
     return;
 }
 
diff --git a/system/GLESv2/gl2.cpp b/system/GLESv2/gl2.cpp
index b75f899..331da78 100644
--- a/system/GLESv2/gl2.cpp
+++ b/system/GLESv2/gl2.cpp
@@ -88,26 +88,32 @@
     }
 }
 
-void glEGLImageTargetRenderbufferStorageOES(void *self, GLenum target, GLeglImageOES image)
+void glEGLImageTargetRenderbufferStorageOES(void *self, GLenum target, GLeglImageOES img)
 {
     (void)self;
     (void)target;
 
-    DBG("glEGLImageTargetRenderbufferStorageOES v2 image=%p\n", image);
+    DBG("glEGLImageTargetRenderbufferStorageOES v2 image=%p\n", img);
     //TODO: check error - we don't have a way to set gl error
-    android_native_buffer_t* native_buffer = (android_native_buffer_t*)image;
+    EGLImage_t *image = (EGLImage_t*)img;
 
-    if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
-        return;
+    if (image->target == EGL_NATIVE_BUFFER_ANDROID) {
+        android_native_buffer_t* native_buffer = ((EGLImage_t*)image)->native_buffer;
+
+        if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
+            return;
+        }
+
+        if (native_buffer->common.version != sizeof(android_native_buffer_t)) {
+            return;
+        }
+
+        DEFINE_AND_VALIDATE_HOST_CONNECTION();
+        rcEnc->rcBindRenderbuffer(rcEnc, ((cb_handle_t *)(native_buffer->handle))->hostHandle);
+    } else {
+        //TODO
     }
 
-    if (native_buffer->common.version != sizeof(android_native_buffer_t)) {
-        return;
-    }
-
-    DEFINE_AND_VALIDATE_HOST_CONNECTION();
-    rcEnc->rcBindRenderbuffer(rcEnc, ((cb_handle_t *)(native_buffer->handle))->hostHandle);
-
     return;
 }
 
diff --git a/system/GLESv2_enc/Android.mk b/system/GLESv2_enc/Android.mk
index c5081d3..f61c9f7 100644
--- a/system/GLESv2_enc/Android.mk
+++ b/system/GLESv2_enc/Android.mk
@@ -10,6 +10,7 @@
     gl2_client_context.cpp \
     gl2_enc.cpp \
     gl2_entry.cpp \
+    ../enc_common/IOStream_common.cpp \
 
 LOCAL_CFLAGS += -DLOG_TAG=\"emuglGLESv2_enc\"
 
diff --git a/system/GLESv2_enc/GL2Encoder.cpp b/system/GLESv2_enc/GL2Encoder.cpp
index 39e4cda..cbc0c32 100755
--- a/system/GLESv2_enc/GL2Encoder.cpp
+++ b/system/GLESv2_enc/GL2Encoder.cpp
@@ -73,6 +73,7 @@
     m_currMajorVersion = 2;
     m_currMinorVersion = 0;
     m_initialized = false;
+    m_noHostError = false;
     m_state = NULL;
     m_error = GL_NO_ERROR;
     m_num_compressedTextureFormats = 0;
@@ -195,6 +196,8 @@
     OVERRIDEOES(glDeleteVertexArrays);
     OVERRIDEOES(glBindVertexArray);
 
+    OVERRIDE_CUSTOM(glMapBufferOES);
+    OVERRIDE_CUSTOM(glUnmapBufferOES);
     OVERRIDE_CUSTOM(glMapBufferRange);
     OVERRIDE_CUSTOM(glUnmapBuffer);
     OVERRIDE_CUSTOM(glFlushMappedBufferRange);
@@ -363,7 +366,11 @@
         return err;
     }
 
-    return ctx->m_glGetError_enc(self);
+    if (ctx->m_noHostError) {
+        return GL_NO_ERROR;
+    } else {
+        return ctx->m_glGetError_enc(self);
+    }
 }
 
 class GL2Encoder::ErrorUpdater {
@@ -629,6 +636,9 @@
     GLClientState* state = ctx->m_state;
 
     switch (param) {
+    case GL_NUM_EXTENSIONS:
+        *ptr = (int)ctx->m_currExtensionsArray.size();
+        break;
     case GL_MAJOR_VERSION:
         *ptr = ctx->m_deviceMajorVersion;
         break;
@@ -1321,42 +1331,73 @@
 //      #define SAMPLER(TYPE, NAME) uniform sampler#TYPE NAME
 //      SAMPLER(ExternalOES, mySampler);
 //
-static bool replaceSamplerExternalWith2D(char* const str, ShaderData* const data)
-{
-    static const char STR_HASH_EXTENSION[] = "#extension";
-    static const char STR_GL_OES_EGL_IMAGE_EXTERNAL[] = "GL_OES_EGL_image_external";
-    static const char STR_SAMPLER_EXTERNAL_OES[] = "samplerExternalOES";
-    static const char STR_SAMPLER2D_SPACE[]      = "sampler2D         ";
 
-    // -- overwrite all "#extension GL_OES_EGL_image_external : xxx" statements
+static const char STR_SAMPLER_EXTERNAL_OES[] = "samplerExternalOES";
+static const char STR_SAMPLER2D_SPACE[]      = "sampler2D         ";
+static const char STR_DEFINE[] = "#define";
+
+static std::vector<std::string> getSamplerExternalAliases(char* str) {
+    std::vector<std::string> res;
+
+    res.push_back(STR_SAMPLER_EXTERNAL_OES);
+
+    // -- capture #define x samplerExternalOES
     char* c = str;
-    while ((c = strstr(c, STR_HASH_EXTENSION))) {
-        char* start = c;
-        c += sizeof(STR_HASH_EXTENSION)-1;
-        while (isspace(*c) && *c != '\0') {
-            c++;
-        }
-        if (strncmp(c, STR_GL_OES_EGL_IMAGE_EXTERNAL,
-                sizeof(STR_GL_OES_EGL_IMAGE_EXTERNAL)-1) == 0)
-        {
-            // #extension statements are terminated by end of line
-            c = start;
-            while (*c != '\0' && *c != '\r' && *c != '\n') {
-                *c++ = ' ';
+    while ((c = strstr(c, STR_DEFINE))) {
+        // Don't push it if samplerExternalOES is not even there.
+        char* samplerExternalOES_next = strstr(c, STR_SAMPLER_EXTERNAL_OES);
+        if (!samplerExternalOES_next) break;
+
+        bool prevIdent = false;
+
+        std::vector<std::string> idents;
+        std::string curr;
+
+        while (*c != '\0') {
+
+            if (isspace(*c)) {
+                if (prevIdent) {
+                    idents.push_back(curr);
+                    curr = "";
+                }
             }
+
+            if (*c == '\n' || idents.size() == 3) break;
+
+            if (isalpha(*c) || *c == '_') {
+                curr.push_back(*c);
+                prevIdent = true;
+            }
+
+            ++c;
         }
+
+        if (idents.size() != 3) continue;
+
+        const std::string& defineLhs = idents[1];
+        const std::string& defineRhs = idents[2];
+
+        if (defineRhs == STR_SAMPLER_EXTERNAL_OES) {
+            res.push_back(defineLhs);
+        }
+
+        if (*c == '\0') break;
     }
 
+    return res;
+}
+
+static bool replaceExternalSamplerUniformDefinition(char* str, const std::string& samplerExternalType, ShaderData* data) {
     // -- replace "samplerExternalOES" with "sampler2D" and record name
-    c = str;
-    while ((c = strstr(c, STR_SAMPLER_EXTERNAL_OES))) {
+    char* c = str;
+    while ((c = strstr(c, samplerExternalType.c_str()))) {
         // Make sure "samplerExternalOES" isn't a substring of a larger token
         if (c == str || !isspace(*(c-1))) {
             c++;
             continue;
         }
         char* sampler_start = c;
-        c += sizeof(STR_SAMPLER_EXTERNAL_OES)-1;
+        c += samplerExternalType.size();
         if (!isspace(*c) && *c != '\0') {
             continue;
         }
@@ -1376,8 +1417,58 @@
         data->samplerExternalNames.push_back(
                 android::String8(name_start, c - name_start));
 
-        // memcpy instead of strcpy since we don't want the NUL terminator
-        memcpy(sampler_start, STR_SAMPLER2D_SPACE, sizeof(STR_SAMPLER2D_SPACE)-1);
+        // We only need to perform a string replacement for the original
+        // occurrence of samplerExternalOES if a #define was used.
+        //
+        // The important part was to record the name in
+        // |data->samplerExternalNames|.
+        if (samplerExternalType == STR_SAMPLER_EXTERNAL_OES) {
+            memcpy(sampler_start, STR_SAMPLER2D_SPACE, sizeof(STR_SAMPLER2D_SPACE)-1);
+        }
+    }
+
+    return true;
+}
+
+static bool replaceSamplerExternalWith2D(char* const str, ShaderData* const data)
+{
+    static const char STR_HASH_EXTENSION[] = "#extension";
+    static const char STR_GL_OES_EGL_IMAGE_EXTERNAL[] = "GL_OES_EGL_image_external";
+    static const char STR_GL_OES_EGL_IMAGE_EXTERNAL_ESSL3[] = "GL_OES_EGL_image_external_essl3";
+
+    // -- overwrite all "#extension GL_OES_EGL_image_external : xxx" statements
+    char* c = str;
+    while ((c = strstr(c, STR_HASH_EXTENSION))) {
+        char* start = c;
+        c += sizeof(STR_HASH_EXTENSION)-1;
+        while (isspace(*c) && *c != '\0') {
+            c++;
+        }
+
+        bool hasBaseImageExternal =
+            !strncmp(c, STR_GL_OES_EGL_IMAGE_EXTERNAL,
+                     sizeof(STR_GL_OES_EGL_IMAGE_EXTERNAL) - 1);
+        bool hasEssl3ImageExternal =
+            !strncmp(c, STR_GL_OES_EGL_IMAGE_EXTERNAL_ESSL3,
+                     sizeof(STR_GL_OES_EGL_IMAGE_EXTERNAL_ESSL3) - 1);
+
+        if (hasBaseImageExternal || hasEssl3ImageExternal)
+        {
+            // #extension statements are terminated by end of line
+            c = start;
+            while (*c != '\0' && *c != '\r' && *c != '\n') {
+                *c++ = ' ';
+            }
+        }
+    }
+
+    std::vector<std::string> samplerExternalAliases =
+        getSamplerExternalAliases(str);
+
+    for (size_t i = 0; i < samplerExternalAliases.size(); i++) {
+        if (!replaceExternalSamplerUniformDefinition(
+                str, samplerExternalAliases[i], data))
+            return false;
     }
 
     return true;
@@ -2579,6 +2670,27 @@
     state->setVertexArrayObject(array);
 }
 
+void* GL2Encoder::s_glMapBufferOES(void* self, GLenum target, GLenum access) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+
+    RET_AND_SET_ERROR_IF(!GLESv2Validation::bufferTarget(ctx, target), GL_INVALID_ENUM, NULL);
+
+    GLuint boundBuffer = ctx->m_state->getBuffer(target);
+
+    RET_AND_SET_ERROR_IF(boundBuffer == 0, GL_INVALID_OPERATION, NULL);
+
+    BufferData* buf = ctx->m_shared->getBufferData(boundBuffer);
+    RET_AND_SET_ERROR_IF(!buf, GL_INVALID_VALUE, NULL);
+
+    return ctx->glMapBufferRange(ctx, target, 0, buf->m_size, access);
+}
+
+GLboolean GL2Encoder::s_glUnmapBufferOES(void* self, GLenum target) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+
+    return ctx->glUnmapBuffer(ctx, target);
+}
+
 void* GL2Encoder::s_glMapBufferRange(void* self, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {
     GL2Encoder* ctx = (GL2Encoder*)self;
     GLClientState* state = ctx->m_state;
@@ -2739,6 +2851,10 @@
     state->setBoundTextureInternalFormat(stateTarget, (GLint)internalformat);
     state->setBoundTextureDims(stateTarget, level, width, height, 1);
 
+    if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
+        ctx->override2DTextureTarget(target);
+    }
+
     if (ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER)) {
         ctx->glCompressedTexImage2DOffsetAEMU(
                 ctx, target, level, internalformat,
@@ -2750,6 +2866,10 @@
                 width, height, border,
                 imageSize, data);
     }
+
+    if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
+        ctx->restore2DTextureTarget(target);
+    }
 }
 
 void GL2Encoder::s_glCompressedTexSubImage2D(void* self, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data) {
@@ -2774,6 +2894,10 @@
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(xoffset < 0 || yoffset < 0, GL_INVALID_VALUE);
 
+    if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
+        ctx->override2DTextureTarget(target);
+    }
+
     if (ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER)) {
         ctx->glCompressedTexSubImage2DOffsetAEMU(
                 ctx, target, level,
@@ -2787,6 +2911,10 @@
                 width, height, format,
                 imageSize, data);
     }
+
+    if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
+        ctx->restore2DTextureTarget(target);
+    }
 }
 
 void GL2Encoder::s_glBindBufferRange(void* self, GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) {
@@ -3383,7 +3511,16 @@
     state->setBoundTextureInternalFormat(target, internalformat);
     state->setBoundTextureDims(target, -1, width, height, 1);
     state->setBoundTextureImmutableFormat(target);
+
+    if (target == GL_TEXTURE_2D) {
+        ctx->override2DTextureTarget(target);
+    }
+
     ctx->m_glTexStorage2D_enc(ctx, target, levels, internalformat, width, height);
+
+    if (target == GL_TEXTURE_2D) {
+        ctx->restore2DTextureTarget(target);
+    }
 }
 
 void GL2Encoder::s_glTransformFeedbackVaryings(void* self, GLuint program, GLsizei count, const char** varyings, GLenum bufferMode) {
@@ -3873,7 +4010,7 @@
 
 const GLubyte* GL2Encoder::s_glGetStringi(void* self, GLenum name, GLuint index) {
     GL2Encoder *ctx = (GL2Encoder *)self;
-    GLubyte *retval =  (GLubyte *) "";
+    const GLubyte *retval =  (GLubyte *) "";
 
     RET_AND_SET_ERROR_IF(
         name != GL_VENDOR &&
@@ -3887,11 +4024,16 @@
         name == GL_VENDOR ||
         name == GL_RENDERER ||
         name == GL_VERSION ||
-        name == GL_EXTENSIONS &&
         index != 0,
         GL_INVALID_VALUE,
         retval);
 
+    RET_AND_SET_ERROR_IF(
+        name == GL_EXTENSIONS &&
+        index >= ctx->m_currExtensionsArray.size(),
+        GL_INVALID_VALUE,
+        retval);
+
     switch (name) {
     case GL_VENDOR:
         retval = gVendorString;
@@ -3903,7 +4045,7 @@
         retval = gVersionString;
         break;
     case GL_EXTENSIONS:
-        retval = gExtensionsString;
+        retval = (const GLubyte*)(ctx->m_currExtensionsArray[index].c_str());
         break;
     }
 
@@ -4148,7 +4290,8 @@
 
     SET_ERROR_IF(target != GL_TEXTURE_2D &&
                  target != GL_TEXTURE_3D &&
-                 target != GL_TEXTURE_CUBE_MAP,
+                 target != GL_TEXTURE_CUBE_MAP &&
+                 target != GL_TEXTURE_2D_ARRAY,
                  GL_INVALID_ENUM);
 
     GLuint tex = state->getBoundTexture(target);
@@ -4163,7 +4306,15 @@
                    GLESv2Validation::filterableTexFormat(ctx, internalformat)),
                  GL_INVALID_OPERATION);
 
+    if (target == GL_TEXTURE_2D) {
+        ctx->override2DTextureTarget(target);
+    }
+
     ctx->m_glGenerateMipmap_enc(ctx, target);
+
+    if (target == GL_TEXTURE_2D) {
+        ctx->restore2DTextureTarget(target);
+    }
 }
 
 void GL2Encoder::s_glBindSampler(void* self, GLuint unit, GLuint sampler) {
diff --git a/system/GLESv2_enc/GL2Encoder.h b/system/GLESv2_enc/GL2Encoder.h
index 3084713..3018622 100644
--- a/system/GLESv2_enc/GL2Encoder.h
+++ b/system/GLESv2_enc/GL2Encoder.h
@@ -22,11 +22,15 @@
 #include "FixedBuffer.h"
 
 #include <string>
+#include <vector>
 
 class GL2Encoder : public gl2_encoder_context_t {
 public:
     GL2Encoder(IOStream *stream, ChecksumCalculator* protocol);
     virtual ~GL2Encoder();
+    void setNoHostError(bool noHostError) {
+        m_noHostError = noHostError;
+    }
     void setClientState(GLClientState *state) {
         m_state = state;
     }
@@ -56,8 +60,10 @@
     }
     int majorVersion() const { return m_currMajorVersion; }
     int minorVersion() const { return m_currMinorVersion; }
-    void setExtensions(const char* exts) {
+    void setExtensions(const char* exts,
+                       const std::vector<std::string>& extArray) {
         m_currExtensions = std::string(exts);
+        m_currExtensionsArray = extArray;
     }
     bool hasExtension(const char* ext) const {
         return m_currExtensions.find(ext) != std::string::npos;
@@ -90,8 +96,10 @@
     int m_deviceMajorVersion;
     int m_deviceMinorVersion;
     std::string m_currExtensions;
+    std::vector<std::string> m_currExtensionsArray;
 
     bool    m_initialized;
+    bool    m_noHostError;
     GLClientState *m_state;
     GLSharedGroupPtr m_shared;
     GLenum  m_error;
@@ -391,6 +399,8 @@
     static void s_glBindVertexArray(void *self , GLuint array);
 
     // Mapped buffers
+    static void* s_glMapBufferOES(void* self, GLenum target, GLenum access);
+    static GLboolean s_glUnmapBufferOES(void* self, GLenum target);
     static void* s_glMapBufferRange(void* self, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
     static GLboolean s_glUnmapBuffer(void* self, GLenum target);
     static void s_glFlushMappedBufferRange(void* self, GLenum target, GLintptr offset, GLsizeiptr length);
diff --git a/system/GLESv2_enc/gl2_enc.cpp b/system/GLESv2_enc/gl2_enc.cpp
index 8357c68..f42e7ba 100644
--- a/system/GLESv2_enc/gl2_enc.cpp
+++ b/system/GLESv2_enc/gl2_enc.cpp
@@ -3028,7 +3028,10 @@
 	if (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);
 	if (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize;
 
-	stream->readback(pixels, __size_pixels);
+    // TODO: make this part of autogenerated pipe code.
+    // b/79208762
+	stream->readbackPixels(self, width, height, format, type, pixels);
+
 	if (useChecksum) checksumCalculator->addBuffer(pixels, __size_pixels);
 	if (useChecksum) {
 		unsigned char *checksumBufPtr = NULL;
diff --git a/system/OpenglSystemCommon/HostConnection.cpp b/system/OpenglSystemCommon/HostConnection.cpp
index ce89021..34ddef4 100644
--- a/system/OpenglSystemCommon/HostConnection.cpp
+++ b/system/OpenglSystemCommon/HostConnection.cpp
@@ -37,7 +37,8 @@
     m_rcEnc(NULL),
     m_checksumHelper(),
     m_glExtensions(),
-    m_grallocOnly(true)
+    m_grallocOnly(true),
+    m_noHostError(false)
 {
 }
 
@@ -146,6 +147,7 @@
         m_gl2Enc = new GL2Encoder(m_stream, checksumHelper());
         DBG("HostConnection::gl2Encoder new encoder %p, tid %d", m_gl2Enc, gettid());
         m_gl2Enc->setContextAccessor(s_getGL2Context);
+        m_gl2Enc->setNoHostError(m_noHostError);
     }
     return m_gl2Enc;
 }
@@ -158,6 +160,7 @@
         queryAndSetSyncImpl(m_rcEnc);
         queryAndSetDmaImpl(m_rcEnc);
         queryAndSetGLESMaxVersion(m_rcEnc);
+        queryAndSetNoErrorState(m_rcEnc);
         processPipeInit(m_rcEnc);
     }
     return m_rcEnc;
@@ -233,8 +236,10 @@
 #if PLATFORM_SDK_VERSION <= 16 || (!defined(__i386__) && !defined(__x86_64__))
     rcEnc->setSyncImpl(SYNC_IMPL_NONE);
 #else
-    if (glExtensions.find(kRCNativeSync) != std::string::npos) {
-        rcEnc->setSyncImpl(SYNC_IMPL_NATIVE_SYNC);
+    if (glExtensions.find(kRCNativeSyncV3) != std::string::npos) {
+        rcEnc->setSyncImpl(SYNC_IMPL_NATIVE_SYNC_V3);
+    } else if (glExtensions.find(kRCNativeSyncV2) != std::string::npos) {
+        rcEnc->setSyncImpl(SYNC_IMPL_NATIVE_SYNC_V2);
     } else {
         rcEnc->setSyncImpl(SYNC_IMPL_NONE);
     }
@@ -270,3 +275,10 @@
         rcEnc->setGLESMaxVersion(GLES_MAX_VERSION_2);
     }
 }
+
+void HostConnection::queryAndSetNoErrorState(ExtendedRCEncoderContext* rcEnc) {
+    std::string glExtensions = queryGLExtensions(rcEnc);
+    if (glExtensions.find(kGLESNoHostError) != std::string::npos) {
+        m_noHostError = true;
+    }
+}
diff --git a/system/OpenglSystemCommon/HostConnection.h b/system/OpenglSystemCommon/HostConnection.h
index 25d42d6..2b49857 100644
--- a/system/OpenglSystemCommon/HostConnection.h
+++ b/system/OpenglSystemCommon/HostConnection.h
@@ -40,16 +40,15 @@
 // capability, and we will use a fence fd to synchronize buffer swaps.
 enum SyncImpl {
     SYNC_IMPL_NONE = 0,
-    SYNC_IMPL_NATIVE_SYNC = 1
+    SYNC_IMPL_NATIVE_SYNC_V2 = 1,
+    SYNC_IMPL_NATIVE_SYNC_V3 = 2,
 };
 
 // Interface:
-// If this GL extension string shows up, we use
-// SYNC_IMPL_NATIVE_SYNC, otherwise we use SYNC_IMPL_NONE.
-// This string is always updated to require the _latest_
-// version of Android emulator native sync in this system image;
-// otherwise, we do not use the feature.
-static const char kRCNativeSync[] = "ANDROID_EMU_native_sync_v2";
+// Use the highest of v2 or v3 that show up, making us
+// SYNC_IMPL_NATIVE_SYNC_V2 or SYNC_IMPL_NATIVE_SYNC_V3.
+static const char kRCNativeSyncV2[] = "ANDROID_EMU_native_sync_v2";
+static const char kRCNativeSyncV3[] = "ANDROID_EMU_native_sync_v3";
 
 // DMA for OpenGL
 enum DmaImpl {
@@ -72,6 +71,9 @@
 static const char kGLESMaxVersion_3_1[] = "ANDROID_EMU_gles_max_version_3_1";
 static const char kGLESMaxVersion_3_2[] = "ANDROID_EMU_gles_max_version_3_2";
 
+// No querying errors from host extension
+static const char kGLESNoHostError[] = "ANDROID_EMU_gles_no_host_error";
+
 // ExtendedRCEncoderContext is an extended version of renderControl_encoder_context_t
 // that will be used to track SyncImpl.
 class ExtendedRCEncoderContext : public renderControl_encoder_context_t {
@@ -82,7 +84,8 @@
         }
     void setSyncImpl(SyncImpl syncImpl) { m_syncImpl = syncImpl; }
     void setDmaImpl(DmaImpl dmaImpl) { m_dmaImpl = dmaImpl; }
-    bool hasNativeSync() const { return m_syncImpl == SYNC_IMPL_NATIVE_SYNC; }
+    bool hasNativeSync() const { return m_syncImpl >= SYNC_IMPL_NATIVE_SYNC_V2; }
+    bool hasNativeSyncV3() const { return m_syncImpl >= SYNC_IMPL_NATIVE_SYNC_V3; }
     DmaImpl getDmaVersion() const { return m_dmaImpl; }
     void bindDmaContext(struct goldfish_dma_context* cxt) { m_dmaCxt = cxt; }
     virtual uint64_t lockAndWriteDma(void* data, uint32_t size) {
@@ -148,6 +151,7 @@
     void queryAndSetSyncImpl(ExtendedRCEncoderContext *rcEnc);
     void queryAndSetDmaImpl(ExtendedRCEncoderContext *rcEnc);
     void queryAndSetGLESMaxVersion(ExtendedRCEncoderContext *rcEnc);
+    void queryAndSetNoErrorState(ExtendedRCEncoderContext *rcEnc);
 
 private:
     IOStream *m_stream;
@@ -158,6 +162,7 @@
     std::string m_glExtensions;
     bool m_grallocOnly;
     int m_pipeFd;
+    bool m_noHostError;
 };
 
 #endif
diff --git a/system/egl/egl.cpp b/system/egl/egl.cpp
index 185fbc9..16285f9 100644
--- a/system/egl/egl.cpp
+++ b/system/egl/egl.cpp
@@ -160,12 +160,14 @@
     }
 
 #define VALIDATE_CONTEXT_RETURN(context,ret)  \
-    if (!(context)) {                         \
+    if (!(context) || !s_display.isContext((context))) {                         \
         RETURN_ERROR(ret,EGL_BAD_CONTEXT);    \
     }
 
 #define VALIDATE_SURFACE_RETURN(surface, ret)    \
     if ((surface) != EGL_NO_SURFACE) {    \
+        if (!s_display.isSurface((surface))) \
+            setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE); \
         egl_surface_t* s( static_cast<egl_surface_t*>(surface) );    \
         if (s->dpy != (EGLDisplay)&s_display)    \
             setErrorReturn(EGL_BAD_DISPLAY, EGL_FALSE);    \
@@ -282,7 +284,9 @@
 
     virtual     void setCollectingTimestamps(EGLint collect) { }
     virtual     EGLint isCollectingTimestamps() const { return EGL_FALSE; }
-
+    EGLint      deletePending;
+    void        setIsCurrent(bool isCurrent) { mIsCurrent = isCurrent; }
+    bool        isCurrent() const { return mIsCurrent;}
 private:
     //
     //Surface attributes
@@ -296,7 +300,7 @@
     // Give it some default values.
     int nativeWidth;
     int nativeHeight;
-
+    bool mIsCurrent;
 protected:
     void        setWidth(EGLint w)  { width = w;  }
     void        setHeight(EGLint h) { height = h; }
@@ -308,7 +312,8 @@
 };
 
 egl_surface_t::egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLint surfaceType)
-    : dpy(dpy), config(config), surfaceType(surfaceType), rcSurface(0)
+    : dpy(dpy), config(config), surfaceType(surfaceType), rcSurface(0),
+      deletePending(0), mIsCurrent(false)
 {
     width = 0;
     height = 0;
@@ -603,6 +608,34 @@
     }
 }
 
+// Destroy a pending surface and set it to NULL.
+
+static void s_destroyPendingSurfaceAndSetNull(EGLSurface* surface) {
+    if (!s_display.isSurface(surface)) {
+        *surface = NULL;
+        return;
+    }
+
+    egl_surface_t* surf = static_cast<egl_surface_t *>(*surface);
+    if (surf && surf->deletePending) {
+        delete surf;
+        *surface = NULL;
+    }
+}
+
+static void s_destroyPendingSurfacesInContext(EGLContext_t* context) {
+    if (context->read == context->draw) {
+        // If they are the same, delete it only once
+        s_destroyPendingSurfaceAndSetNull(&context->draw);
+        if (context->draw == NULL) {
+            context->read = NULL;
+        }
+    } else {
+        s_destroyPendingSurfaceAndSetNull(&context->draw);
+        s_destroyPendingSurfaceAndSetNull(&context->read);
+    }
+}
+
 EGLBoolean egl_pbuffer_surface_t::init(GLenum pixelFormat)
 {
     DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
@@ -638,6 +671,68 @@
     return pb;
 }
 
+// Required for Skia.
+static const char kOESEGLImageExternalEssl3[] = "GL_OES_EGL_image_external_essl3";
+
+static bool sWantES30OrAbove(const char* exts) {
+    if (strstr(exts, kGLESMaxVersion_3_0) ||
+        strstr(exts, kGLESMaxVersion_3_1) ||
+        strstr(exts, kGLESMaxVersion_3_2)) {
+        return true;
+    }
+    return false;
+}
+
+static std::vector<std::string> getExtStringArray() {
+    std::vector<std::string> res;
+
+    EGLThreadInfo *tInfo = getEGLThreadInfo();
+    if (!tInfo || !tInfo->currentContext) {
+        return res;
+    }
+
+#define GL_EXTENSIONS                     0x1F03
+
+    DEFINE_AND_VALIDATE_HOST_CONNECTION(res);
+
+    char *hostStr = NULL;
+    int n = rcEnc->rcGetGLString(rcEnc, GL_EXTENSIONS, NULL, 0);
+    if (n < 0) {
+        hostStr = new char[-n+1];
+        n = rcEnc->rcGetGLString(rcEnc, GL_EXTENSIONS, hostStr, -n);
+        if (n <= 0) {
+            delete [] hostStr;
+            hostStr = NULL;
+        }
+    }
+
+    if (!hostStr || !strlen(hostStr)) { return res; }
+
+    // find the number of extensions
+    int extStart = 0;
+    int extEnd = 0;
+    int currentExtIndex = 0;
+    int numExts = 0;
+
+    if (sWantES30OrAbove(hostStr) &&
+        !strstr(hostStr, kOESEGLImageExternalEssl3)) {
+        res.push_back(kOESEGLImageExternalEssl3);
+    }
+
+    while (extEnd < strlen(hostStr)) {
+        if (hostStr[extEnd] == ' ') {
+            int extSz = extEnd - extStart;
+            res.push_back(std::string(hostStr + extStart, extSz));
+            currentExtIndex++;
+            extStart = extEnd + 1;
+        }
+        extEnd++;
+    }
+
+    delete [] hostStr;
+    return res;
+}
+
 static const char *getGLString(int glEnum)
 {
     EGLThreadInfo *tInfo = getEGLThreadInfo();
@@ -675,18 +770,42 @@
         return NULL;
     }
 
-    //
-    // first query of that string - need to query host
-    //
-    DEFINE_AND_VALIDATE_HOST_CONNECTION(NULL);
-    char *hostStr = NULL;
-    int n = rcEnc->rcGetGLString(rcEnc, glEnum, NULL, 0);
-    if (n < 0) {
-        hostStr = new char[-n+1];
-        n = rcEnc->rcGetGLString(rcEnc, glEnum, hostStr, -n);
-        if (n <= 0) {
-            delete [] hostStr;
-            hostStr = NULL;
+    char* hostStr = NULL;
+
+    if (glEnum == GL_EXTENSIONS) {
+
+        std::vector<std::string> exts = getExtStringArray();
+
+        int totalSz = 1; // null terminator
+        for (int i = 0; i < exts.size(); i++) {
+            totalSz += exts[i].size() + 1; // for space
+        }
+
+        if (totalSz == 1) return NULL;
+
+        hostStr = new char[totalSz];
+        memset(hostStr, 0, totalSz);
+
+        char* current = hostStr;
+        for (int i = 0; i < exts.size(); i++) {
+            memcpy(current, exts[i].c_str(), exts[i].size());
+            current += exts[i].size();
+            *current = ' ';
+            ++current;
+        }
+    } else {
+        //
+        // first query of that string - need to query host
+        //
+        DEFINE_AND_VALIDATE_HOST_CONNECTION(NULL);
+        int n = rcEnc->rcGetGLString(rcEnc, glEnum, NULL, 0);
+        if (n < 0) {
+            hostStr = new char[-n+1];
+            n = rcEnc->rcGetGLString(rcEnc, glEnum, hostStr, -n);
+            if (n <= 0) {
+                delete [] hostStr;
+                hostStr = NULL;
+            }
         }
     }
 
@@ -701,7 +820,7 @@
 
 static EGLClient_eglInterface s_eglIface = {
     getThreadInfo: getEGLThreadInfo,
-    getGLString: getGLString
+    getGLString: getGLString,
 };
 
 #define DBG_FUNC DBG("%s\n", __FUNCTION__)
@@ -984,8 +1103,13 @@
     VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
     VALIDATE_SURFACE_RETURN(eglSurface, EGL_FALSE);
 
+    EGLThreadInfo* tInfo = getEGLThreadInfo();
     egl_surface_t* surface(static_cast<egl_surface_t*>(eglSurface));
-    delete surface;
+    if (surface->isCurrent()) {
+        surface->deletePending = 1;
+    } else {
+        delete surface;
+    }
 
     return EGL_TRUE;
 }
@@ -1150,7 +1274,7 @@
     tInfo->eglError = EGL_SUCCESS;
     EGLContext_t* context = tInfo->currentContext;
 
-    if (!context) return EGL_TRUE;
+    if (!context || !s_display.isContext(context)) return EGL_TRUE;
 
     // The following code is doing pretty much the same thing as
     // eglMakeCurrent(&s_display, EGL_NO_CONTEXT, EGL_NO_SURFACE, EGL_NO_SURFACE)
@@ -1160,6 +1284,9 @@
     // anyway once we are on the host, so skip rcMakeCurrent here.
     // rcEnc->rcMakeCurrent(rcEnc, 0, 0, 0);
     context->flags &= ~EGLContext_t::IS_CURRENT;
+
+    s_destroyPendingSurfacesInContext(context);
+
     if (context->deletePending) {
         if (context->rcContext) {
             rcEnc->rcDestroyContext(rcEnc, context->rcContext);
@@ -1342,17 +1469,12 @@
                 RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE);
             }
             break;
-        case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR:
-            switch (attrib_val) {
-            case EGL_NO_RESET_NOTIFICATION_KHR:
-            case EGL_LOSE_CONTEXT_ON_RESET_KHR:
-                break;
-            default:
-                RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE);
-            }
-            reset_notification_strategy = attrib_val;
+        case EGL_CONTEXT_PRIORITY_LEVEL_IMG:
+            // According to the spec, we are allowed not to honor this hint.
+            // https://www.khronos.org/registry/EGL/extensions/IMG/EGL_IMG_context_priority.txt
             break;
         default:
+            ALOGV("eglCreateContext unsupported attrib 0x%x", attrib_list[0]);
             setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
         }
         attrib_list+=2;
@@ -1472,10 +1594,8 @@
 
     EGLContext_t * context = static_cast<EGLContext_t*>(ctx);
 
-    if (!context) return EGL_TRUE;
-
-    if (getEGLThreadInfo()->currentContext == context) {
-        getEGLThreadInfo()->currentContext->deletePending = 1;
+    if (context->flags & EGLContext_t::IS_CURRENT) {
+        context->deletePending = 1;
         return EGL_TRUE;
     }
 
@@ -1518,15 +1638,24 @@
 
     if (tInfo->currentContext == context &&
         (context == NULL ||
-        (context && context->draw == draw && context->read == read))) {
+        (context && (context->draw == draw) && (context->read == read)))) {
         return EGL_TRUE;
     }
 
-    if (tInfo->currentContext && tInfo->currentContext->deletePending) {
-        if (tInfo->currentContext != context) {
-            EGLContext_t * contextToDelete = tInfo->currentContext;
+    if (tInfo->currentContext) {
+        EGLContext_t* prevCtx = tInfo->currentContext;
+
+        if (prevCtx->draw) {
+            static_cast<egl_surface_t *>(prevCtx->draw)->setIsCurrent(false);
+        }
+        if (prevCtx->read) {
+            static_cast<egl_surface_t *>(prevCtx->read)->setIsCurrent(false);
+        }
+        s_destroyPendingSurfacesInContext(tInfo->currentContext);
+
+        if (prevCtx->deletePending && prevCtx != context) {
             tInfo->currentContext = 0;
-            eglDestroyContext(dpy, contextToDelete);
+            eglDestroyContext(dpy, prevCtx);
         }
     }
 
@@ -1551,6 +1680,12 @@
         hostCon->setGrallocOnly(false);
         context->draw = draw;
         context->read = read;
+        if (drawSurf) {
+            drawSurf->setIsCurrent(true);
+        }
+        if (readSurf) {
+            readSurf->setIsCurrent(true);
+        }
         context->flags |= EGLContext_t::IS_CURRENT;
         GLClientState* contextState =
             context->getClientState();
@@ -1633,8 +1768,9 @@
 
     }
 
-    if (tInfo->currentContext)
+    if (tInfo->currentContext && (tInfo->currentContext != context)) {
         tInfo->currentContext->flags &= ~EGLContext_t::IS_CURRENT;
+    }
 
     //Now make current
     tInfo->currentContext = context;
@@ -1649,7 +1785,7 @@
             }
             const char* exts = getGLString(GL_EXTENSIONS);
             if (exts) {
-                hostCon->gl2Encoder()->setExtensions(exts);
+                hostCon->gl2Encoder()->setExtensions(exts, getExtStringArray());
             }
         }
         else {
@@ -1823,6 +1959,8 @@
             case HAL_PIXEL_FORMAT_RGB_565:
             case HAL_PIXEL_FORMAT_YV12:
             case HAL_PIXEL_FORMAT_BGRA_8888:
+            case HAL_PIXEL_FORMAT_RGBA_FP16:
+            case HAL_PIXEL_FORMAT_RGBA_1010102:
                 break;
             default:
                 setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
@@ -2097,10 +2235,24 @@
     }
 }
 
-// TODO: Implement EGL_KHR_wait_sync
 EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR eglsync, EGLint flags) {
     (void)dpy;
-    (void)eglsync;
-    (void)flags;
+
+    if (!eglsync) {
+        ALOGE("%s: null sync object!", __FUNCTION__);
+        return EGL_FALSE;
+    }
+
+    if (flags) {
+        ALOGE("%s: flags must be 0, got 0x%x", __FUNCTION__, flags);
+        return EGL_FALSE;
+    }
+
+    DEFINE_HOST_CONNECTION;
+    if (rcEnc->hasNativeSyncV3()) {
+        EGLSync_t* sync = (EGLSync_t*)eglsync;
+        rcEnc->rcWaitSyncKHR(rcEnc, sync->handle, flags);
+    }
+
     return EGL_TRUE;
 }
diff --git a/system/egl/eglDisplay.cpp b/system/egl/eglDisplay.cpp
index c7ff179..f593598 100644
--- a/system/egl/eglDisplay.cpp
+++ b/system/egl/eglDisplay.cpp
@@ -33,6 +33,7 @@
 
 // extensions to add dynamically depending on host-side support
 static const char kDynamicEGLExtNativeSync[] = "EGL_ANDROID_native_fence_sync ";
+static const char kDynamicEGLExtWaitSync[] = "EGL_KHR_wait_sync ";
 
 static void *s_gles_lib = NULL;
 static void *s_gles2_lib = NULL;
@@ -347,6 +348,10 @@
         if (hcon->rcEncoder()->hasNativeSync() &&
             !strstr(initialEGLExts, kDynamicEGLExtNativeSync)) {
             dynamicEGLExtensions += kDynamicEGLExtNativeSync;
+
+            if (hcon->rcEncoder()->hasNativeSyncV3()) {
+                dynamicEGLExtensions += kDynamicEGLExtWaitSync;
+            }
         }
 
         asprintf(&finalEGLExts, "%s%s", initialEGLExts, dynamicEGLExtensions.c_str());
@@ -572,3 +577,16 @@
     pthread_mutex_unlock(&m_surfaceLock);
 }
 
+bool eglDisplay::isContext(EGLContext ctx) {
+    pthread_mutex_lock(&m_ctxLock);
+    bool res = m_contexts.find(ctx) != m_contexts.end();
+    pthread_mutex_unlock(&m_ctxLock);
+    return res;
+}
+
+bool eglDisplay::isSurface(EGLSurface surface) {
+    pthread_mutex_lock(&m_surfaceLock);
+    bool res = m_surfaces.find(surface) != m_surfaces.end();
+    pthread_mutex_unlock(&m_surfaceLock);
+    return res;
+}
diff --git a/system/egl/eglDisplay.h b/system/egl/eglDisplay.h
index ba39201..9fb6f07 100644
--- a/system/egl/eglDisplay.h
+++ b/system/egl/eglDisplay.h
@@ -67,6 +67,9 @@
 
     void onDestroyContext(EGLContext ctx);
     void onDestroySurface(EGLSurface surface);
+
+    bool isContext(EGLContext ctx);
+    bool isSurface(EGLSurface ctx);
 private:
     EGLClient_glesInterface *loadGLESClientAPI(const char *libName,
                                                EGLClient_eglInterface *eglIface,
diff --git a/system/enc_common/IOStream_common.cpp b/system/enc_common/IOStream_common.cpp
new file mode 100644
index 0000000..43f03af
--- /dev/null
+++ b/system/enc_common/IOStream_common.cpp
@@ -0,0 +1,59 @@
+#include "IOStream.h"
+
+#include "GL2Encoder.h"
+
+#include <GLES3/gl31.h>
+
+#include <vector>
+
+void IOStream::readbackPixels(void* context, int width, int height, unsigned int format, unsigned int type, void* pixels) {
+    GL2Encoder *ctx = (GL2Encoder *)context;
+    assert (ctx->state() != NULL);
+
+    int startOffset = 0;
+    int pixelRowSize = 0;
+    int totalRowSize = 0;
+    int skipRows = 0;
+
+    ctx->state()->getPackingOffsets2D(width, height, format, type,
+                                      &startOffset,
+                                      &pixelRowSize,
+                                      &totalRowSize,
+                                      &skipRows);
+
+    size_t pixelDataSize =
+        ctx->state()->pixelDataSize(
+            width, height, 1, format, type, 1 /* is pack */);
+
+    if (startOffset == 0 &&
+        pixelRowSize == totalRowSize) {
+        // fast path
+        readback(pixels, pixelDataSize);
+    } else if (pixelRowSize == totalRowSize) {
+        // fast path but with skip in the beginning
+        std::vector<char> paddingToDiscard(startOffset, 0);
+        readback(&paddingToDiscard[0], startOffset);
+        readback((char*)pixels + startOffset, pixelDataSize - startOffset);
+    } else {
+        int totalReadback = 0;
+
+        if (startOffset > 0) {
+            std::vector<char> paddingToDiscard(startOffset, 0);
+            readback(&paddingToDiscard[0], startOffset);
+            totalReadback += startOffset;
+        }
+        // need to read back row by row
+        size_t paddingSize = totalRowSize - pixelRowSize;
+        std::vector<char> paddingToDiscard(paddingSize, 0);
+
+        char* start = (char*)pixels + startOffset;
+
+        for (int i = 0; i < height; i++) {
+            readback(start, pixelRowSize);
+            totalReadback += pixelRowSize;
+            readback(&paddingToDiscard[0], paddingSize);
+            totalReadback += paddingSize;
+            start += totalRowSize;
+        }
+    }
+}
diff --git a/system/gralloc/gralloc.cpp b/system/gralloc/gralloc.cpp
index 223f87a..cabc27b 100644
--- a/system/gralloc/gralloc.cpp
+++ b/system/gralloc/gralloc.cpp
@@ -118,6 +118,7 @@
 
 struct gralloc_memregions_t {
     MemRegionSet ashmemRegions;
+    pthread_mutex_t lock;
 };
 
 #define INITIAL_DMA_REGION_SIZE 4096
@@ -126,15 +127,17 @@
     uint32_t sz;
     uint32_t refcount;
     pthread_mutex_t lock;
+    uint32_t bigbufCount;
 };
 
 // global device instance
-static gralloc_memregions_t* s_grdev = NULL;
+static gralloc_memregions_t* s_memregions = NULL;
 static gralloc_dmaregion_t* s_grdma = NULL;
 
 void init_gralloc_memregions() {
-    if (s_grdev) return;
-    s_grdev = new gralloc_memregions_t;
+    if (s_memregions) return;
+    s_memregions = new gralloc_memregions_t;
+    pthread_mutex_init(&s_memregions->lock, NULL);
 }
 
 void init_gralloc_dmaregion() {
@@ -144,6 +147,7 @@
     s_grdma = new gralloc_dmaregion_t;
     s_grdma->sz = INITIAL_DMA_REGION_SIZE;
     s_grdma->refcount = 0;
+    s_grdma->bigbufCount = 0;
 
     pthread_mutex_init(&s_grdma->lock, NULL);
     pthread_mutex_lock(&s_grdma->lock);
@@ -169,11 +173,18 @@
     s_grdma->sz = new_sz;
 }
 
-bool put_gralloc_dmaregion() {
+// max dma size: 2x 4K rgba8888
+#define MAX_DMA_SIZE 66355200
+
+bool put_gralloc_dmaregion(uint32_t sz) {
     if (!s_grdma) return false;
     pthread_mutex_lock(&s_grdma->lock);
     D("%s: call. refcount before: %u\n", __FUNCTION__, s_grdma->refcount);
     s_grdma->refcount--;
+    if (sz > MAX_DMA_SIZE &&
+        s_grdma->bigbufCount) {
+        s_grdma->bigbufCount--;
+    }
     bool shouldDelete = !s_grdma->refcount;
     if (shouldDelete) {
         D("%s: should delete!\n", __FUNCTION__);
@@ -191,8 +202,14 @@
     D("%s: for sz %u, refcount %u", __FUNCTION__, sz, s_grdma->refcount);
     uint32_t new_sz = std::max(s_grdma->sz, sz);
     if (new_sz != s_grdma->sz) {
-        D("%s: change sz from %u to %u", __FUNCTION__, s_grdma->sz, sz);
-        resize_gralloc_dmaregion_locked(new_sz);
+        if (new_sz > MAX_DMA_SIZE)  {
+            D("%s: requested sz %u too large (limit %u), set to fallback.",
+              __FUNCTION__, sz, MAX_DMA_SIZE);
+            s_grdma->bigbufCount++;
+        } else {
+            D("%s: change sz from %u to %u", __FUNCTION__, s_grdma->sz, sz);
+            resize_gralloc_dmaregion_locked(new_sz);
+        }
     }
     if (!s_grdma->goldfish_dma.mapped) {
         goldfish_dma_map(&s_grdma->goldfish_dma);
@@ -205,15 +222,17 @@
     D("%s: call for %p", __FUNCTION__, ashmemBase);
     MemRegionInfo lookup;
     lookup.ashmemBase = ashmemBase;
-    mem_region_handle_t handle = s_grdev->ashmemRegions.find(lookup);
-    if (handle == s_grdev->ashmemRegions.end()) {
+    pthread_mutex_lock(&s_memregions->lock);
+    mem_region_handle_t handle = s_memregions->ashmemRegions.find(lookup);
+    if (handle == s_memregions->ashmemRegions.end()) {
         MemRegionInfo newRegion;
         newRegion.ashmemBase = ashmemBase;
         newRegion.refCount = 1;
-        s_grdev->ashmemRegions.insert(newRegion);
+        s_memregions->ashmemRegions.insert(newRegion);
     } else {
         handle->refCount++;
     }
+    pthread_mutex_unlock(&s_memregions->lock);
 }
 
 bool put_mem_region(void* ashmemBase) {
@@ -221,25 +240,28 @@
     D("%s: call for %p", __FUNCTION__, ashmemBase);
     MemRegionInfo lookup;
     lookup.ashmemBase = ashmemBase;
-    mem_region_handle_t handle = s_grdev->ashmemRegions.find(lookup);
-    if (handle == s_grdev->ashmemRegions.end()) {
+    pthread_mutex_lock(&s_memregions->lock);
+    mem_region_handle_t handle = s_memregions->ashmemRegions.find(lookup);
+    if (handle == s_memregions->ashmemRegions.end()) {
         ALOGE("%s: error: tried to put nonexistent mem region!", __FUNCTION__);
+        pthread_mutex_unlock(&s_memregions->lock);
         return true;
     } else {
         handle->refCount--;
         bool shouldRemove = !handle->refCount;
         if (shouldRemove) {
-            s_grdev->ashmemRegions.erase(lookup);
+            s_memregions->ashmemRegions.erase(lookup);
         }
+        pthread_mutex_unlock(&s_memregions->lock);
         return shouldRemove;
     }
 }
 
 void dump_regions() {
     init_gralloc_memregions();
-    mem_region_handle_t curr = s_grdev->ashmemRegions.begin();
+    mem_region_handle_t curr = s_memregions->ashmemRegions.begin();
     std::stringstream res;
-    for (; curr != s_grdev->ashmemRegions.end(); curr++) {
+    for (; curr != s_memregions->ashmemRegions.end(); curr++) {
         res << "\tashmem base " << curr->ashmemBase << " refcount " << curr->refCount << "\n";
     }
     ALOGD("ashmem region dump [\n%s]", res.str().c_str());
@@ -354,7 +376,12 @@
                 width, height, top, left, bpp);
     }
 
-    if (s_grdma) {
+    if (s_grdma->bigbufCount) {
+        D("%s: there are big buffers alive, use fallback (count %u)", __FUNCTION__,
+          s_grdma->bigbufCount);
+    }
+
+    if (s_grdma && !s_grdma->bigbufCount) {
         if (cb->frameworkFormat == HAL_PIXEL_FORMAT_YV12) {
             get_yv12_offsets(width, height, NULL, NULL,
                              &send_buffer_size);
@@ -394,12 +421,15 @@
 #ifndef GL_RGBA16F
 #define GL_RGBA16F                        0x881A
 #endif // GL_RGBA16F
-#ifndef GL_UNSIGNED_INT_10_10_10_2
-#define GL_UNSIGNED_INT_10_10_10_2        0x8DF6
-#endif // GL_UNSIGNED_INT_10_10_10_2
 #ifndef GL_HALF_FLOAT
 #define GL_HALF_FLOAT                     0x140B
 #endif // GL_HALF_FLOAT
+#ifndef GL_RGB10_A2
+#define GL_RGB10_A2                       0x8059
+#endif // GL_RGB10_A2
+#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
+#define GL_UNSIGNED_INT_2_10_10_10_REV    0x8368
+#endif // GL_UNSIGNED_INT_2_10_10_10_REV
 //
 // gralloc device functions (alloc interface)
 //
@@ -496,7 +526,10 @@
             break;
         case HAL_PIXEL_FORMAT_RGB_565:
             bpp = 2;
-            glFormat = GL_RGB;
+            // Workaround: distinguish vs the RGB8/RGBA8
+            // by changing |glFormat| to GL_RGB565
+            // (previously, it was still GL_RGB)
+            glFormat = GL_RGB565;
             glType = GL_UNSIGNED_SHORT_5_6_5;
             break;
 #if PLATFORM_SDK_VERSION >= 26
@@ -507,8 +540,8 @@
             break;
         case HAL_PIXEL_FORMAT_RGBA_1010102:
             bpp = 4;
-            glFormat = GL_RGBA;
-            glType = GL_UNSIGNED_INT_10_10_10_2;
+            glFormat = GL_RGB10_A2;
+            glType = GL_UNSIGNED_INT_2_10_10_10_REV;
             break;
 #endif // PLATFORM_SDK_VERSION >= 26
 #if PLATFORM_SDK_VERSION >= 21
@@ -577,7 +610,11 @@
     // rendering will still happen on the host but we also need to be able to
     // read back from the color buffer, which requires that there is a buffer
     //
+#if PLATFORM_SDK_VERSION >= 17
+    bool needHostCb = ((!yuv_format && frameworkFormat != HAL_PIXEL_FORMAT_BLOB) ||
+#else
     bool needHostCb = (!yuv_format ||
+#endif
                        frameworkFormat == HAL_PIXEL_FORMAT_YV12 ||
                        frameworkFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) &&
 #if PLATFORM_SDK_VERSION >= 15
@@ -678,10 +715,19 @@
 
     if (needHostCb) {
         if (hostCon && rcEnc) {
+            GLenum allocFormat = glFormat;
+            // The handling of RGBX_8888 is very subtle. Most of the time
+            // we want it to be treated as RGBA_8888, with the exception
+            // that alpha is always ignored and treated as 1. The solution
+            // is to create 3 channel RGB texture instead and host GL will
+            // handle the Alpha channel.
+            if (HAL_PIXEL_FORMAT_RGBX_8888 == format) {
+                allocFormat = GL_RGB;
+            }
             if (s_grdma) {
-                cb->hostHandle = rcEnc->rcCreateColorBufferDMA(rcEnc, w, h, glFormat, cb->emuFrameworkFormat);
+                cb->hostHandle = rcEnc->rcCreateColorBufferDMA(rcEnc, w, h, allocFormat, cb->emuFrameworkFormat);
             } else {
-                cb->hostHandle = rcEnc->rcCreateColorBuffer(rcEnc, w, h, glFormat);
+                cb->hostHandle = rcEnc->rcCreateColorBuffer(rcEnc, w, h, allocFormat);
             }
             D("Created host ColorBuffer 0x%x\n", cb->hostHandle);
         }
@@ -760,7 +806,7 @@
         if (cb->ashmemSize > 0 && cb->ashmemBase) {
             D("%s: unmapped %p", __FUNCTION__, cb->ashmemBase);
             munmap((void *)cb->ashmemBase, cb->ashmemSize);
-            put_gralloc_dmaregion();
+            put_gralloc_dmaregion(cb->ashmemSize);
         }
         close(cb->fd);
     }
@@ -914,8 +960,6 @@
         return sFallback->registerBuffer(sFallback, handle);
     }
 
-    D("gralloc_register_buffer(%p) called", handle);
-
     private_module_t *gr = (private_module_t *)module;
     cb_handle_t *cb = (cb_handle_t *)handle;
 
@@ -924,6 +968,9 @@
         return -EINVAL;
     }
 
+    D("gralloc_register_buffer(%p) w %d h %d format 0x%x framworkFormat 0x%x",
+        handle, cb->width, cb->height, cb->format, cb->frameworkFormat);
+
     if (cb->hostHandle != 0) {
         DEFINE_AND_VALIDATE_HOST_CONNECTION;
         D("Opening host ColorBuffer 0x%x\n", cb->hostHandle);
@@ -1006,7 +1053,7 @@
     if (cb->ashmemSize > 0 && cb->mappedPid == getpid()) {
 
         PUT_ASHMEM_REGION(cb);
-        put_gralloc_dmaregion();
+        put_gralloc_dmaregion(cb->ashmemSize);
 
         if (!SHOULD_UNMAP) goto done;
 
@@ -1169,8 +1216,8 @@
         cb->lockedHeight = h;
     }
 
-    DD("gralloc_lock success. vaddr: %p, *vaddr: %p, usage: %x, cpu_addr: %p",
-            vaddr, vaddr ? *vaddr : 0, usage, cpu_addr);
+    DD("gralloc_lock success. vaddr: %p, *vaddr: %p, usage: %x, cpu_addr: %p, base: %p",
+            vaddr, vaddr ? *vaddr : 0, usage, cpu_addr, cb->ashmemBase);
 
     return 0;
 }
@@ -1288,14 +1335,12 @@
             cStep = 1;
             break;
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
-            align = 1;
             yStride = cb->width;
-            cStride = yStride / 2;
+            cStride = cb->width;
             yOffset = 0;
-            cSize = cStride * cb->height/2;
-            uOffset = yStride * cb->height;
-            vOffset = uOffset + cSize;
-            cStep = 1;
+            vOffset = yStride * cb->height;
+            uOffset = vOffset + 1;
+            cStep = 2;
             break;
         default:
             ALOGE("gralloc_lock_ycbcr unexpected internal format %x",
@@ -1322,9 +1367,9 @@
     cb->lockedHeight = h;
 
     DD("gralloc_lock_ycbcr success. usage: %x, ycbcr.y: %p, .cb: %p, .cr: %p, "
-            ".ystride: %d , .cstride: %d, .chroma_step: %d", usage,
+            ".ystride: %d , .cstride: %d, .chroma_step: %d, base: %p", usage,
             ycbcr->y, ycbcr->cb, ycbcr->cr, ycbcr->ystride, ycbcr->cstride,
-            ycbcr->chroma_step);
+            ycbcr->chroma_step, cb->ashmemBase);
 
     return 0;
 }
diff --git a/system/renderControl_enc/renderControl_client_context.cpp b/system/renderControl_enc/renderControl_client_context.cpp
index 617e8f9..4a86244 100644
--- a/system/renderControl_enc/renderControl_client_context.cpp
+++ b/system/renderControl_enc/renderControl_client_context.cpp
@@ -46,6 +46,7 @@
 	rcSetPuid = (rcSetPuid_client_proc_t) getProc("rcSetPuid", userData);
 	rcUpdateColorBufferDMA = (rcUpdateColorBufferDMA_client_proc_t) getProc("rcUpdateColorBufferDMA", userData);
 	rcCreateColorBufferDMA = (rcCreateColorBufferDMA_client_proc_t) getProc("rcCreateColorBufferDMA", userData);
+	rcWaitSyncKHR = (rcWaitSyncKHR_client_proc_t) getProc("rcWaitSyncKHR", userData);
 	return 0;
 }
 
diff --git a/system/renderControl_enc/renderControl_client_context.h b/system/renderControl_enc/renderControl_client_context.h
index 7a3e3f6..1e5594b 100644
--- a/system/renderControl_enc/renderControl_client_context.h
+++ b/system/renderControl_enc/renderControl_client_context.h
@@ -46,6 +46,7 @@
 	rcSetPuid_client_proc_t rcSetPuid;
 	rcUpdateColorBufferDMA_client_proc_t rcUpdateColorBufferDMA;
 	rcCreateColorBufferDMA_client_proc_t rcCreateColorBufferDMA;
+	rcWaitSyncKHR_client_proc_t rcWaitSyncKHR;
 	virtual ~renderControl_client_context_t() {}
 
 	typedef renderControl_client_context_t *CONTEXT_ACCESSOR_TYPE(void);
diff --git a/system/renderControl_enc/renderControl_client_proc.h b/system/renderControl_enc/renderControl_client_proc.h
index e517d07..3f1e5bb 100644
--- a/system/renderControl_enc/renderControl_client_proc.h
+++ b/system/renderControl_enc/renderControl_client_proc.h
@@ -45,6 +45,7 @@
 typedef void (renderControl_APIENTRY *rcSetPuid_client_proc_t) (void * ctx, uint64_t);
 typedef int (renderControl_APIENTRY *rcUpdateColorBufferDMA_client_proc_t) (void * ctx, uint32_t, GLint, GLint, GLint, GLint, GLenum, GLenum, void*, uint32_t);
 typedef uint32_t (renderControl_APIENTRY *rcCreateColorBufferDMA_client_proc_t) (void * ctx, uint32_t, uint32_t, GLenum, int);
+typedef void (renderControl_APIENTRY *rcWaitSyncKHR_client_proc_t) (void * ctx, uint64_t, EGLint);
 
 
 #endif
diff --git a/system/renderControl_enc/renderControl_enc.cpp b/system/renderControl_enc/renderControl_enc.cpp
index 3c4a3f3..e22d7c0 100644
--- a/system/renderControl_enc/renderControl_enc.cpp
+++ b/system/renderControl_enc/renderControl_enc.cpp
@@ -1365,6 +1365,32 @@
 	return retval;
 }
 
+void rcWaitSyncKHR_enc(void *self , uint64_t sync, EGLint flags)
+{
+
+	renderControl_encoder_context_t *ctx = (renderControl_encoder_context_t *)self;
+	IOStream *stream = ctx->m_stream;
+	ChecksumCalculator *checksumCalculator = ctx->m_checksumCalculator;
+	bool useChecksum = checksumCalculator->getVersion() > 0;
+
+	 unsigned char *ptr;
+	 unsigned char *buf;
+	 const size_t sizeWithoutChecksum = 8 + 8 + 4;
+	 const size_t checksumSize = checksumCalculator->checksumByteSize();
+	 const size_t totalSize = sizeWithoutChecksum + checksumSize;
+	buf = stream->alloc(totalSize);
+	ptr = buf;
+	int tmp = OP_rcWaitSyncKHR;memcpy(ptr, &tmp, 4); ptr += 4;
+	memcpy(ptr, &totalSize, 4);  ptr += 4;
+
+		memcpy(ptr, &sync, 8); ptr += 8;
+		memcpy(ptr, &flags, 4); ptr += 4;
+
+	if (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);
+	if (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize;
+
+}
+
 }  // namespace
 
 renderControl_encoder_context_t::renderControl_encoder_context_t(IOStream *stream, ChecksumCalculator *checksumCalculator)
@@ -1408,5 +1434,6 @@
 	this->rcSetPuid = &rcSetPuid_enc;
 	this->rcUpdateColorBufferDMA = &rcUpdateColorBufferDMA_enc;
 	this->rcCreateColorBufferDMA = &rcCreateColorBufferDMA_enc;
+	this->rcWaitSyncKHR = &rcWaitSyncKHR_enc;
 }
 
diff --git a/system/renderControl_enc/renderControl_entry.cpp b/system/renderControl_enc/renderControl_entry.cpp
index 101806e..9a47dd3 100644
--- a/system/renderControl_enc/renderControl_entry.cpp
+++ b/system/renderControl_enc/renderControl_entry.cpp
@@ -41,6 +41,7 @@
 	void rcSetPuid(uint64_t puid);
 	int rcUpdateColorBufferDMA(uint32_t colorbuffer, GLint x, GLint y, GLint width, GLint height, GLenum format, GLenum type, void* pixels, uint32_t pixels_size);
 	uint32_t rcCreateColorBufferDMA(uint32_t width, uint32_t height, GLenum internalFormat, int frameworkFormat);
+	void rcWaitSyncKHR(uint64_t sync, EGLint flags);
 };
 
 #ifndef GET_CONTEXT
@@ -265,3 +266,9 @@
 	return ctx->rcCreateColorBufferDMA(ctx, width, height, internalFormat, frameworkFormat);
 }
 
+void rcWaitSyncKHR(uint64_t sync, EGLint flags)
+{
+	GET_CONTEXT;
+	ctx->rcWaitSyncKHR(ctx, sync, flags);
+}
+
diff --git a/system/renderControl_enc/renderControl_ftable.h b/system/renderControl_enc/renderControl_ftable.h
index 4f2a09e..7bfc7c5 100644
--- a/system/renderControl_enc/renderControl_ftable.h
+++ b/system/renderControl_enc/renderControl_ftable.h
@@ -44,6 +44,7 @@
 	{"rcSetPuid", (void*)rcSetPuid},
 	{"rcUpdateColorBufferDMA", (void*)rcUpdateColorBufferDMA},
 	{"rcCreateColorBufferDMA", (void*)rcCreateColorBufferDMA},
+	{"rcWaitSyncKHR", (void*)rcWaitSyncKHR},
 };
 static const int renderControl_num_funcs = sizeof(renderControl_funcs_by_name) / sizeof(struct _renderControl_funcs_by_name);
 
diff --git a/system/renderControl_enc/renderControl_opcodes.h b/system/renderControl_enc/renderControl_opcodes.h
index 43d8f30..146164f 100644
--- a/system/renderControl_enc/renderControl_opcodes.h
+++ b/system/renderControl_enc/renderControl_opcodes.h
@@ -39,7 +39,8 @@
 #define OP_rcSetPuid 					10033
 #define OP_rcUpdateColorBufferDMA 					10034
 #define OP_rcCreateColorBufferDMA 					10035
-#define OP_last 					10036
+#define OP_rcWaitSyncKHR 					10036
+#define OP_last 					10037
 
 
 #endif