Move host validation to guest

Test: dEQP-GLES3 no failures when not using host-side glGetError

Change-Id: If602d7c784ea12c5ae47ce56a91669e14a9ac62f
diff --git a/Android.mk b/Android.mk
index 90a4b84..c0b270a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -133,6 +133,11 @@
 include $(GOLDFISH_OPENGL_PATH)/shared/qemupipe/Android.mk
 include $(GOLDFISH_OPENGL_PATH)/shared/gralloc_cb/Android.mk
 include $(GOLDFISH_OPENGL_PATH)/shared/GoldfishAddressSpace/Android.mk
+
+ifeq (true,$(GFXSTREAM)) # android-emu
+    include $(GOLDFISH_OPENGL_PATH)/android-emu/Android.mk
+endif
+
 include $(GOLDFISH_OPENGL_PATH)/shared/OpenglCodecCommon/Android.mk
 
 # Encoder shared libraries
@@ -141,7 +146,6 @@
 include $(GOLDFISH_OPENGL_PATH)/system/renderControl_enc/Android.mk
 
 ifeq (true,$(GFXSTREAM)) # Vulkan libs
-    include $(GOLDFISH_OPENGL_PATH)/android-emu/Android.mk
     include $(GOLDFISH_OPENGL_PATH)/system/vulkan_enc/Android.mk
 endif
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4066666..d3442ab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,15 +2,15 @@
 # instead run make from .../device/generic/goldfish-opengl
 # which will re-generate this file.
 set(GOLDFISH_DEVICE_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
-android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/./Android.mk" "a4e5ce1679876aa9b240f5976e8782d3d2390d3de39ec783bf24f5947f7c920c")
+android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/./Android.mk" "4ef1864208e48039ca8ead2df33a20d5eb6ac512fc456cd2fd701011071c29fe")
 add_subdirectory(shared/qemupipe)
 add_subdirectory(shared/gralloc_cb)
 add_subdirectory(shared/GoldfishAddressSpace)
+add_subdirectory(android-emu)
 add_subdirectory(shared/OpenglCodecCommon)
 add_subdirectory(system/GLESv1_enc)
 add_subdirectory(system/GLESv2_enc)
 add_subdirectory(system/renderControl_enc)
-add_subdirectory(android-emu)
 add_subdirectory(system/vulkan_enc)
 add_subdirectory(system/OpenglSystemCommon)
 add_subdirectory(system/GLESv1)
diff --git a/android-emu/CMakeLists.txt b/android-emu/CMakeLists.txt
index 7ee2b43..74d5e46 100644
--- a/android-emu/CMakeLists.txt
+++ b/android-emu/CMakeLists.txt
@@ -7,4 +7,4 @@
 target_include_directories(androidemu PRIVATE ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(androidemu PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"androidemu\"")
 target_compile_options(androidemu PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-fstrict-aliasing")
-target_link_libraries(androidemu PRIVATE cutils utils log OpenglCodecCommon_host android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
+target_link_libraries(androidemu PRIVATE cutils utils log android-emu-shared)
\ No newline at end of file
diff --git a/shared/OpenglCodecCommon/Android.mk b/shared/OpenglCodecCommon/Android.mk
index ba2b412..46d3fda 100644
--- a/shared/OpenglCodecCommon/Android.mk
+++ b/shared/OpenglCodecCommon/Android.mk
@@ -13,6 +13,7 @@
         SocketStream.cpp \
         TcpStream.cpp \
         auto_goldfish_dma_context.cpp \
+        etc.cpp \
 
 ifeq (true,$(GOLDFISH_OPENGL_BUILD_FOR_HOST))
 
@@ -42,7 +43,8 @@
 
 
 ifeq (true,$(GOLDFISH_OPENGL_BUILD_FOR_HOST))
-$(call emugl-export,SHARED_LIBRARIES,libcutils libutils liblog android-emu-shared)
+$(call emugl-import,libandroidemu)
+$(call emugl-export,SHARED_LIBRARIES,libcutils libutils liblog)
 else
 ifeq (true,$(GFXSTREAM))
 $(call emugl-export,SHARED_LIBRARIES,libcutils libutils liblog libandroidemu)
diff --git a/shared/OpenglCodecCommon/CMakeLists.txt b/shared/OpenglCodecCommon/CMakeLists.txt
index 01fe056..545525f 100644
--- a/shared/OpenglCodecCommon/CMakeLists.txt
+++ b/shared/OpenglCodecCommon/CMakeLists.txt
@@ -1,10 +1,10 @@
 # This is an autogenerated file! Do not edit!
 # instead run make from .../device/generic/goldfish-opengl
 # which will re-generate this file.
-android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon/Android.mk" "b86c7ce26ad32ec1fc3080efc119211a77d696104f340b59e33882984bffe97a")
-set(OpenglCodecCommon_host_src GLClientState.cpp GLESTextureUtils.cpp ChecksumCalculator.cpp GLSharedGroup.cpp glUtils.cpp IndexRangeCache.cpp SocketStream.cpp TcpStream.cpp auto_goldfish_dma_context.cpp goldfish_dma_host.cpp)
-android_add_library(TARGET OpenglCodecCommon_host SHARED LICENSE Apache-2.0 SRC GLClientState.cpp GLESTextureUtils.cpp ChecksumCalculator.cpp GLSharedGroup.cpp glUtils.cpp IndexRangeCache.cpp SocketStream.cpp TcpStream.cpp auto_goldfish_dma_context.cpp goldfish_dma_host.cpp)
-target_include_directories(OpenglCodecCommon_host PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon/Android.mk" "a9f19fd167bcc8bf102e288cccc0c4a780fa6671ff52f1f94c3b5b26d7482728")
+set(OpenglCodecCommon_host_src GLClientState.cpp GLESTextureUtils.cpp ChecksumCalculator.cpp GLSharedGroup.cpp glUtils.cpp IndexRangeCache.cpp SocketStream.cpp TcpStream.cpp auto_goldfish_dma_context.cpp etc.cpp goldfish_dma_host.cpp)
+android_add_library(TARGET OpenglCodecCommon_host SHARED LICENSE Apache-2.0 SRC GLClientState.cpp GLESTextureUtils.cpp ChecksumCalculator.cpp GLSharedGroup.cpp glUtils.cpp IndexRangeCache.cpp SocketStream.cpp TcpStream.cpp auto_goldfish_dma_context.cpp etc.cpp goldfish_dma_host.cpp)
+target_include_directories(OpenglCodecCommon_host PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(OpenglCodecCommon_host PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"eglCodecCommon\"")
 target_compile_options(OpenglCodecCommon_host PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-unused-private-field")
-target_link_libraries(OpenglCodecCommon_host PRIVATE cutils utils log android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
+target_link_libraries(OpenglCodecCommon_host PRIVATE cutils utils log androidemu android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
diff --git a/shared/OpenglCodecCommon/GLClientState.cpp b/shared/OpenglCodecCommon/GLClientState.cpp
index f61f133..039b763 100644
--- a/shared/OpenglCodecCommon/GLClientState.cpp
+++ b/shared/OpenglCodecCommon/GLClientState.cpp
@@ -32,12 +32,12 @@
 #endif
 
 // Don't include these in the .h file, or we get weird compile errors.
+#include <GLES2/gl2ext.h>
 #include <GLES3/gl3.h>
 #include <GLES3/gl31.h>
 
 void GLClientState::init() {
     m_initialized = false;
-    m_nLocations = CODEC_MAX_VERTEX_ATTRIBUTES;
 
     m_arrayBuffer = 0;
     m_arrayBuffer_lastEncode = 0;
@@ -48,7 +48,6 @@
     m_vaoAttribBindingHasVboCache = 0;
     m_noClientArraysCache = 0;
 
-    m_max_vertex_attrib_bindings = m_nLocations;
     addVertexArrayObject(0);
     setVertexArrayObject(0);
     // init gl constans;
@@ -78,13 +77,9 @@
     m_drawIndirectBuffer = 0;
     m_shaderStorageBuffer = 0;
 
-    m_transformFeedbackActiveUnpaused = false;
-
-    // to be modified later when these are queried from host.
-    m_max_transform_feedback_separate_attribs = 0;
-    m_max_uniform_buffer_bindings = 0;
-    m_max_atomic_counter_buffer_bindings = 0;
-    m_max_shader_storage_buffer_bindings = 0;
+    m_transformFeedbackActive = false;
+    m_transformFeedbackUnpaused = false;
+    m_transformFeedbackVaryingsCountForLinking = 0;
 
     m_activeTexture = 0;
     m_currentProgram = 0;
@@ -108,14 +103,28 @@
     m_tex.textureRecs = NULL;
 
     mRboState.boundRenderbuffer = 0;
-    mRboState.boundRenderbufferIndex = 0;
 
     mFboState.boundDrawFramebuffer = 0;
     mFboState.boundReadFramebuffer = 0;
     mFboState.drawFboCheckStatus = GL_NONE;
     mFboState.readFboCheckStatus = GL_NONE;
 
-    m_maxVertexAttribsDirty = true;
+    m_extensions_set = false;
+
+#ifdef GFXSTREAM
+    // The default transform feedback buffer object
+    // The default sampler object
+    GLuint defaultId = 0;
+    setExistence(ObjectType::TransformFeedback, true, 1, &defaultId);
+
+    mBoundTransformFeedbackValidity.id = 0;
+    mBoundTransformFeedbackValidity.valid = true;
+
+    // query must take id that was created via glGenQueries
+    mBoundQueryValidity_AnySamplesPassed.valid = false;
+    mBoundQueryValidity_AnySamplesPassedConservative.valid = false;
+    mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid = false;
+#endif
 }
 
 GLClientState::GLClientState()
@@ -225,10 +234,10 @@
     m_vaoMap.insert(
             VAOStateMap::value_type(
                 name,
-                VAOState(0, m_nLocations, std::max(m_nLocations, m_max_vertex_attrib_bindings))));
+                VAOState(0, CODEC_MAX_VERTEX_ATTRIBUTES, CODEC_MAX_VERTEX_ATTRIBUTES)));
     VertexAttribStateVector& attribState =
         m_vaoMap.find(name)->second.attribState;
-    for (int i = 0; i < m_nLocations; i++) {
+    for (int i = 0; i < CODEC_MAX_VERTEX_ATTRIBUTES; i++) {
         attribState[i].enabled = 0;
         attribState[i].enableDirty = false;
         attribState[i].data = 0;
@@ -446,6 +455,195 @@
     return mHostMappedBufferDirty.get(id);
 }
 
+void GLClientState::setExistence(ObjectType type, bool exists, GLsizei count, const GLuint* ids) {
+    if (type == ObjectType::Sampler) {
+        SamplerInfo::ScopedView view(mSamplerInfo);
+        if (exists) {
+            for (GLsizei i = 0; i < count; ++i) {
+                view.addFresh(ids[i]);
+            }
+        } else {
+            for (GLsizei i = 0; i < count; ++i) {
+                view.unref(ids[i]);
+            }
+        }
+    } else {
+        ExistenceMap* existenceMap = &mBufferIds;
+
+        switch (type) {
+            case ObjectType::Buffer:
+                existenceMap = &mBufferIds;
+                break;
+            case ObjectType::TransformFeedback:
+                existenceMap = &mTransformFeedbackIds;
+                break;
+            case ObjectType::Query:
+                existenceMap = &mQueryIds;
+                for (GLsizei i = 0; i < count; ++i) {
+                    // reset the last query target
+                    mLastQueryTargets.add(ids[i], 0);
+                }
+                break;
+            case ObjectType::Sampler:
+            default:
+                ALOGE("%s: Unreachable code\n", __func__);
+                abort();
+        }
+
+        if (exists) {
+            for (GLsizei i = 0; i < count; ++i) {
+                existenceMap->add(ids[i]);
+                existenceMap->set(ids[i], true);
+            }
+        } else {
+            for (GLsizei i = 0; i < count; ++i) {
+                existenceMap->remove(ids[i]);
+            }
+        }
+    }
+}
+
+bool GLClientState::queryExistence(ObjectType type, GLuint id) const {
+    switch (type) {
+        case ObjectType::Buffer:
+            return mBufferIds.get(id);
+        case ObjectType::TransformFeedback:
+            return mTransformFeedbackIds.get(id);
+        case ObjectType::Sampler:
+            return samplerExists(id);
+        case ObjectType::Query:
+            return mQueryIds.get(id);
+        default:
+            ALOGD("%s: unknown object type: 0x%x\n", __func__, type);
+            abort();
+    }
+}
+
+bool GLClientState::samplerExists(GLuint id) const {
+    if (!id) return true;
+    SamplerInfo::ScopedView view(mSamplerInfo);
+    return view.samplerExists(id);
+}
+
+bool GLClientState::tryBind(GLenum target, GLuint id) {
+    if (0 == id) { // unbind operation
+        switch (target) {
+            case GL_TRANSFORM_FEEDBACK:
+                mBoundTransformFeedbackValidity.id = 0;
+                mBoundTransformFeedbackValidity.valid = true;
+                break;
+            case GL_ANY_SAMPLES_PASSED:
+                mBoundQueryValidity_AnySamplesPassed.id = 0;
+                mBoundQueryValidity_AnySamplesPassed.valid = false;
+                break;
+            case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+                mBoundQueryValidity_AnySamplesPassedConservative.id = 0;
+                mBoundQueryValidity_AnySamplesPassedConservative.valid = false;
+                break;
+            case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+                mBoundQueryValidity_TransformFeedbackPrimitivesWritten.id = 0;
+                mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid = false;
+                break;
+            default:
+                ALOGE("%s: target 0x%x not yet supported in new state tracking model\n", __func__, target);
+                abort();
+        }
+        return true;
+    }
+
+    switch (target) {
+        case GL_TRANSFORM_FEEDBACK:
+            if (!queryExistence(ObjectType::TransformFeedback, id)) return false;
+            break;
+        case GL_ANY_SAMPLES_PASSED:
+        case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+        case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+            if (!queryExistence(ObjectType::Query, id)) {
+                return false;
+            }
+            break;
+        default:
+            ALOGE("%s: target 0x%x not yet supported in new state tracking model\n", __func__, target);
+            abort();
+    }
+
+    // valid bind
+    switch (target) {
+    case GL_TRANSFORM_FEEDBACK:
+        mBoundTransformFeedbackValidity.id = id;
+        mBoundTransformFeedbackValidity.valid = true;
+        break;
+    case GL_ANY_SAMPLES_PASSED:
+        mBoundQueryValidity_AnySamplesPassed.id = id;
+        mBoundQueryValidity_AnySamplesPassed.valid = true;
+        break;
+    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+        mBoundQueryValidity_AnySamplesPassedConservative.id = id;
+        mBoundQueryValidity_AnySamplesPassedConservative.valid = true;
+        break;
+    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+        mBoundQueryValidity_TransformFeedbackPrimitivesWritten.id = id;
+        mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid = true;
+        break;
+    default:
+        ALOGE("%s: target 0x%x not yet supported in new state tracking model\n", __func__, target);
+        abort();
+    }
+    return true;
+}
+
+bool GLClientState::isBoundTargetValid(GLenum target) {
+    switch (target) {
+    case GL_TRANSFORM_FEEDBACK:
+        return mBoundTransformFeedbackValidity.valid;
+    case GL_ANY_SAMPLES_PASSED:
+        return mBoundQueryValidity_AnySamplesPassed.valid;
+    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+        return mBoundQueryValidity_AnySamplesPassedConservative.valid;
+    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+        return mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid;
+    default:
+        ALOGE("%s: target 0x%x not yet supported in new state tracking model\n", __func__, target);
+        abort();
+    }
+}
+
+bool GLClientState::isQueryBound(GLenum target) {
+    switch (target) {
+    case GL_ANY_SAMPLES_PASSED:
+        return mBoundQueryValidity_AnySamplesPassed.valid;
+    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+        return mBoundQueryValidity_AnySamplesPassedConservative.valid;
+    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+        return mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid;
+    default:
+        return false;
+    }
+}
+
+bool GLClientState::isQueryObjectActive(GLuint id) {
+    if (mBoundQueryValidity_AnySamplesPassed.valid &&
+        (id == mBoundQueryValidity_AnySamplesPassed.id))
+        return true;
+    if (mBoundQueryValidity_AnySamplesPassedConservative.valid &&
+        (id == mBoundQueryValidity_AnySamplesPassedConservative.id))
+        return true;
+    if (mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid &&
+        (id == mBoundQueryValidity_TransformFeedbackPrimitivesWritten.id))
+        return true;
+    return false;
+}
+
+void GLClientState::setLastQueryTarget(GLenum target, GLuint id) {
+    mLastQueryTargets.add(id, target);
+}
+
+GLenum GLClientState::getLastQueryTarget(GLuint id) {
+    auto targetPtr = mLastQueryTargets.get_const(id);
+    if (!targetPtr) return 0;
+    return *targetPtr;
+}
+
 #else // GFXSTREAM
 
 void GLClientState::addBuffer(GLuint id) {
@@ -460,16 +658,20 @@
     return mBufferIds.find(id) != mBufferIds.end();
 }
 
-void setBufferHostMapDirty(GLuint id, bool dirty) {
+void GLClientState::setBufferHostMapDirty(GLuint id, bool dirty) {
     (void)id;
     (void)dirty;
 }
 
-bool isBufferHostMapDirty(GLuint id) const {
+bool GLClientState::isBufferHostMapDirty(GLuint id) const {
     (void)id;
     return true;
 }
 
+void GLClientState::setExistence(ObjectType, bool, GLsizei, const GLuint*) {
+    // no-op in non-gfxstream
+}
+
 #endif // !GFXSTREAM
 
 void GLClientState::setBoundPixelPackBufferDirtyForHostMap() {
@@ -706,6 +908,22 @@
     }
 }
 
+int GLClientState::getMaxTextureSize() const {
+    return m_hostDriverCaps.max_texture_size;
+}
+
+int GLClientState::getMaxTextureSize3D() const {
+    return m_hostDriverCaps.max_texture_size_3d;
+}
+
+int GLClientState::getMaxTextureSizeCubeMap() const {
+    return m_hostDriverCaps.max_texture_size_cube_map;
+}
+
+int GLClientState::getLog2MaxTextureSize() const {
+    return m_log2MaxTextureSize;
+}
+
 void GLClientState::postDraw() {
     setBoundTransformFeedbackBuffersDirtyForHostMap();
     setBoundShaderStorageBuffersDirtyForHostMap();
@@ -811,6 +1029,50 @@
     }
 }
 
+bool GLClientState::isTexture(GLuint tex_name) const {
+    return getTextureRec(tex_name);
+}
+
+bool GLClientState::isTextureWithStorage(GLuint tex_name) const {
+    TextureRec* rec = getTextureRec(tex_name);
+    if (!rec) return false;
+    return rec->hasStorage;
+}
+
+bool GLClientState::isTextureCubeMap(GLuint tex_name) const {
+    TextureRec* texrec = getTextureRec(tex_name);
+    if (!texrec) return false;
+    switch (texrec->target) {
+        case GL_TEXTURE_CUBE_MAP:
+        case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+        case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+        case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool GLClientState::isRenderbuffer(GLuint name) const {
+    if (!name) return false;
+
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    return view.hasRbo(name);
+}
+
+bool GLClientState::isRenderbufferThatWasBound(GLuint name) const {
+    if (!name) return true;
+
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    if (!view.hasRbo(name)) return false;
+
+    const RboProps* props = view.get_const(name);
+    return props->previouslyBound;
+}
+
 void GLClientState::getClientStatePointer(GLenum pname, GLvoid** params)
 {
     GLenum which_state = -1;
@@ -1113,8 +1375,14 @@
     }
 }
 
-void GLClientState::bindSampler(GLuint unit, GLuint sampler) {
+bool GLClientState::bindSampler(GLuint unit, GLuint sampler) {
+    SamplerInfo::ScopedView view(mSamplerInfo);
+    view.ref(sampler);
+    if (m_tex.unit[unit].boundSampler) {
+        view.unref(sampler);
+    }
     m_tex.unit[unit].boundSampler = sampler;
+    return true;
 }
 
 bool GLClientState::isSamplerBindNoOp(GLuint unit, GLuint sampler) {
@@ -1164,7 +1432,7 @@
     if (texture && target != texrec->target &&
         (target != GL_TEXTURE_EXTERNAL_OES &&
          texrec->target != GL_TEXTURE_EXTERNAL_OES)) {
-        ALOGD("%s: issue GL_INVALID_OPERATION: target 0x%x texrectarget 0x%x texture %u", __FUNCTION__, target, texrec->target, texture);
+        return GL_INVALID_OPERATION;
     }
 
     switch (target) {
@@ -1198,10 +1466,23 @@
 void GLClientState::setBoundEGLImage(GLenum target, GLeglImageOES image) {
     (void)image;
 
-    GLuint texture = getBoundTexture(target);
-    TextureRec* texrec = getTextureRec(texture);
-    if (!texrec) return;
-    texrec->boundEGLImage = true;
+    if (target == GL_RENDERBUFFER) {
+        if (!boundRenderbuffer()) return;
+        setBoundRenderbufferEGLImageBacked();
+        setBoundRenderbufferFormat(GL_RGBA);
+        setBoundRenderbufferSamples(0);
+        setBoundRenderbufferDimensions(1, 1);
+    } else {
+        GLuint texture = getBoundTexture(target);
+        TextureRec* texrec = getTextureRec(texture);
+        if (!texrec) return;
+        texrec->boundEGLImage = true;
+        setBoundTextureInternalFormat(target, GL_RGBA);
+        setBoundTextureFormat(target, GL_RGBA);
+        setBoundTextureType(target, GL_UNSIGNED_BYTE);
+        setBoundTextureSamples(target, 0);
+        setBoundTextureDims(target, target, 0, 1, 1, 1);
+    }
 }
 
 TextureRec* GLClientState::addTextureRec(GLuint id, GLenum target)
@@ -1213,7 +1494,14 @@
     tex->multisamples = 0;
     tex->immutable = false;
     tex->boundEGLImage = false;
-    tex->dims = new TextureDims;
+    tex->hasStorage = false;
+    tex->dims = new TextureDims[6];
+    tex->hasCubeNegX = false;
+    tex->hasCubePosX = false;
+    tex->hasCubeNegY = false;
+    tex->hasCubePosY = false;
+    tex->hasCubeNegZ = false;
+    tex->hasCubePosZ = false;
 
     (*(m_tex.textureRecs))[id] = tex;
     return tex;
@@ -1249,13 +1537,49 @@
     texrec->type = type;
 }
 
-void GLClientState::setBoundTextureDims(GLenum target, GLsizei level, GLsizei width, GLsizei height, GLsizei depth) {
+static size_t textureDimArrayOfCubeTarget(GLenum cubetarget) {
+    switch (cubetarget) {
+        case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+            return 0;
+        case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+            return 1;
+        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+            return 2;
+        case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+            return 3;
+        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+            return 4;
+        case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+            return 5;
+    }
+    return 0;
+}
+
+void GLClientState::setBoundTextureDims(GLenum target, GLenum cubetarget, GLsizei level, GLsizei width, GLsizei height, GLsizei depth) {
     GLuint texture = getBoundTexture(target);
     TextureRec* texrec = getTextureRec(texture);
     if (!texrec) {
         return;
     }
 
+    texrec->hasStorage = true;
+
+    size_t indexToSet = 0;
+
+    if (target == GL_TEXTURE_CUBE_MAP) {
+        if (-1 == cubetarget) {
+            setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, width, height, depth);
+            setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, width, height, depth);
+            setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, width, height, depth);
+            setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, width, height, depth);
+            setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, width, height, depth);
+            setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, width, height, depth);
+            return;
+        }
+        indexToSet = textureDimArrayOfCubeTarget(cubetarget);
+    }
+
+
     if (level == -1) {
         GLsizei curr_width = width;
         GLsizei curr_height = height;
@@ -1263,9 +1587,9 @@
         GLsizei curr_level = 0;
 
         while (true) {
-            texrec->dims->widths[curr_level] = curr_width;
-            texrec->dims->heights[curr_level] = curr_height;
-            texrec->dims->depths[curr_level] = curr_depth;
+            texrec->dims[indexToSet].widths[curr_level] = curr_width;
+            texrec->dims[indexToSet].heights[curr_level] = curr_height;
+            texrec->dims[indexToSet].depths[curr_level] = curr_depth;
             if (curr_width >> 1 == 0 &&
                 curr_height >> 1 == 0 &&
                 ((target == GL_TEXTURE_3D && curr_depth == 0) ||
@@ -1281,10 +1605,12 @@
         }
 
     } else {
-        texrec->dims->widths[level] = width;
-        texrec->dims->heights[level] = height;
-        texrec->dims->depths[level] = depth;
+        texrec->dims[indexToSet].widths[level] = width;
+        texrec->dims[indexToSet].heights[level] = height;
+        texrec->dims[indexToSet].depths[level] = depth;
     }
+
+    setFboCompletenessDirtyForTexture(texture);
 }
 
 void GLClientState::setBoundTextureSamples(GLenum target, GLsizei samples) {
@@ -1294,11 +1620,48 @@
     texrec->multisamples = samples;
 }
 
+void GLClientState::addTextureCubeMapImage(GLenum stateTarget, GLenum cubeTarget) {
+    if (stateTarget != GL_TEXTURE_CUBE_MAP) return;
+
+    GLuint texture = getBoundTexture(stateTarget);
+    TextureRec* texrec = getTextureRec(texture);
+    if (!texrec) return;
+
+    switch (cubeTarget) {
+        case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+            texrec->hasCubeNegX = true;
+            return;
+        case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+            texrec->hasCubePosX = true;
+            return;
+        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+            texrec->hasCubeNegY = true;
+            return;
+        case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+            texrec->hasCubePosY = true;
+            return;
+        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+            texrec->hasCubeNegZ = true;
+            return;
+        case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+            texrec->hasCubePosZ = true;
+            return;
+    }
+}
+
 void GLClientState::setBoundTextureImmutableFormat(GLenum target) {
     GLuint texture = getBoundTexture(target);
     TextureRec* texrec = getTextureRec(texture);
     if (!texrec) return;
     texrec->immutable = true;
+    if (target == GL_TEXTURE_CUBE_MAP) {
+        texrec->hasCubeNegX = true;
+        texrec->hasCubePosX = true;
+        texrec->hasCubeNegY = true;
+        texrec->hasCubePosY = true;
+        texrec->hasCubeNegZ = true;
+        texrec->hasCubePosZ = true;
+    }
 }
 
 bool GLClientState::isBoundTextureImmutableFormat(GLenum target) const {
@@ -1308,6 +1671,41 @@
     return texrec->immutable;
 }
 
+bool GLClientState::isBoundTextureComplete(GLenum target) const {
+    GLuint texture = getBoundTexture(target);
+    TextureRec* texrec = getTextureRec(texture);
+    if (!texrec) return false;
+
+    if (texrec->immutable) return true;
+    if (!texrec->hasStorage) return true;
+
+    if (target == GL_TEXTURE_CUBE_MAP) {
+        if (!(texrec->hasCubeNegX &&
+             texrec->hasCubePosX &&
+             texrec->hasCubeNegY &&
+             texrec->hasCubePosY &&
+             texrec->hasCubeNegZ &&
+             texrec->hasCubePosZ)) return false;
+
+        size_t currBaseLevel = texrec->dims[0].widths.begin()->first;
+        size_t currWidth = texrec->dims[0].widths.begin()->second;
+        size_t currHeight = texrec->dims[0].heights.begin()->second;
+        for (size_t i = 1; i < 6; ++i) {
+            size_t nextLevel = texrec->dims[i].widths.begin()->first;
+            size_t nextWidth = texrec->dims[i].widths.begin()->second;
+            size_t nextHeight = texrec->dims[i].heights.begin()->second;
+            if (currBaseLevel != nextLevel) return false;
+            if (currWidth != nextWidth) return false;
+            if (currHeight != nextHeight) return false;
+        }
+
+        return true;
+    }
+
+    return true;
+}
+
+
 GLuint GLClientState::getBoundTexture(GLenum target) const
 {
     switch (target) {
@@ -1328,6 +1726,219 @@
     }
 }
 
+GLuint GLClientState::getBoundFramebuffer(GLenum target) const
+{
+    switch (target) {
+    case GL_FRAMEBUFFER:
+    case GL_DRAW_FRAMEBUFFER:
+        return mFboState.boundDrawFramebuffer;
+    case GL_READ_FRAMEBUFFER:
+        return mFboState.boundReadFramebuffer;
+    default:
+        return 0;
+    }
+}
+
+GLenum GLClientState::checkFramebufferCompleteness(GLenum target) {
+    // Default framebuffer is complete
+    // TODO: Check the case where the default framebuffer is 0x0
+    if (0 == boundFramebuffer(target)) return GL_FRAMEBUFFER_COMPLETE;
+
+    bool hasAttachment = false;
+    FboProps& props = boundFboProps(target);
+
+    if (!props.completenessDirty) {
+        return props.cachedCompleteness;
+    }
+
+    int currentSamples = -1;
+
+    for (int i = 0; i < getMaxColorAttachments(); i++) {
+        if (!props.colorAttachmenti_hasTex[i] &&
+            !props.colorAttachmenti_hasRbo[i]) continue;
+
+        GLenum attachmentRes = checkFramebufferAttachmentCompleteness(target, glUtilsColorAttachmentName(i), &currentSamples);
+        if (attachmentRes != GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
+            hasAttachment = true;
+        }
+        if (attachmentRes) {
+            ALOGD("%s: color attachment %d not complete: 0x%x\n", __func__, i, attachmentRes);
+            return attachmentRes;
+        }
+    }
+
+    bool hasDepth = (props.depthAttachment_hasTexObj || props.depthAttachment_hasRbo || props.depthstencilAttachment_hasTexObj || props.depthstencilAttachment_hasRbo);
+    bool hasStencil = (props.stencilAttachment_hasTexObj || props.stencilAttachment_hasRbo || props.depthstencilAttachment_hasTexObj || props.depthstencilAttachment_hasRbo);
+
+    if (hasDepth) {
+        GLenum depthAttachmentRes = checkFramebufferAttachmentCompleteness(target, GL_DEPTH_ATTACHMENT, &currentSamples);
+        if (depthAttachmentRes != GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
+            hasAttachment = true;
+        }
+        if (depthAttachmentRes) {
+            ALOGD("%s: depth attachment not complete: 0x%x\n", __func__, depthAttachmentRes);
+            return depthAttachmentRes;
+        }
+    }
+
+    if (hasStencil) {
+        GLenum stencilAttachmentRes = checkFramebufferAttachmentCompleteness(target, GL_STENCIL_ATTACHMENT, &currentSamples);
+        if (stencilAttachmentRes != GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
+            hasAttachment = true;
+        }
+        if (stencilAttachmentRes) {
+            ALOGD("%s: stencil attachment not complete: 0x%x\n", __func__, stencilAttachmentRes);
+            return stencilAttachmentRes;
+        }
+    }
+
+    if (hasDepth && hasStencil) {
+        // In gles3, depth/stencil must use the same image.
+        if (m_glesMajorVersion > 2) {
+            if ((props.depthAttachment_hasTexObj && props.stencilAttachment_hasRbo) ||
+                (props.stencilAttachment_hasTexObj && props.depthAttachment_hasRbo)) {
+                ALOGD("%s: GL_FRAMEBUFFER_UNSUPPORTED: using different types of depth/stencil attachment images in GLES 3+\n", __func__);
+                return GL_FRAMEBUFFER_UNSUPPORTED;
+            }
+            if (props.depthAttachment_hasTexObj) {
+                if (props.depthAttachment_texture != props.stencilAttachment_texture) {
+                    ALOGD("%s: GL_FRAMEBUFFER_UNSUPPORTED: using different texture images for depth and stencil attachments in GLES 3+\n", __func__);
+                    return GL_FRAMEBUFFER_UNSUPPORTED;
+                }
+            }
+            if (props.depthAttachment_hasRbo) {
+                if (props.depthAttachment_rbo != props.stencilAttachment_rbo) {
+                    ALOGD("%s: GL_FRAMEBUFFER_UNSUPPORTED: using different renderbuffers for depth and stencil attachments in GLES 3+\n", __func__);
+                    return GL_FRAMEBUFFER_UNSUPPORTED;
+                }
+            }
+        }
+    }
+
+    if (!hasAttachment) {
+        return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
+    }
+
+    props.completenessDirty = false;
+    props.cachedCompleteness = GL_FRAMEBUFFER_COMPLETE;
+    return GL_FRAMEBUFFER_COMPLETE;
+}
+
+GLenum GLClientState::checkFramebufferAttachmentCompleteness(GLenum target, GLenum attachment, int* currentSamples) const {
+    FboFormatInfo fbo_format_info;
+    getBoundFramebufferFormat(target, attachment, &fbo_format_info);
+
+    // Check format and renderability
+    bool renderable = false;
+    switch (fbo_format_info.type) {
+        case FBO_ATTACHMENT_RENDERBUFFER:
+            switch (attachment) {
+                case GL_DEPTH_ATTACHMENT:
+                    renderable = fbo_format_info.rb_external || depthRenderableFormat(fbo_format_info.rb_format);
+                    break;
+                case GL_STENCIL_ATTACHMENT:
+                    renderable = fbo_format_info.rb_external || stencilRenderableFormat(fbo_format_info.rb_format);
+                    break;
+                default:
+                    renderable = fbo_format_info.rb_external || colorRenderableFormat(
+                            fbo_format_info.rb_format,
+                            GL_UNSIGNED_BYTE,
+                            m_glesMajorVersion, m_glesMinorVersion,
+                            m_has_color_buffer_float_extension,
+                            m_has_color_buffer_half_float_extension);
+                    if (!renderable) {
+                        ALOGD("%s: rbo not color renderable. format: 0x%x\n", __func__, fbo_format_info.rb_format); }
+                    break;
+            }
+            break;
+        case FBO_ATTACHMENT_TEXTURE:
+            switch (attachment) {
+                case GL_DEPTH_ATTACHMENT:
+                    renderable = fbo_format_info.tex_external || depthRenderableFormat(fbo_format_info.tex_internalformat);
+                    break;
+                case GL_STENCIL_ATTACHMENT:
+                    renderable = fbo_format_info.tex_external || stencilRenderableFormat(fbo_format_info.tex_internalformat);
+                    break;
+                default:
+                    renderable = fbo_format_info.tex_external || colorRenderableFormat(
+                            fbo_format_info.tex_internalformat,
+                            fbo_format_info.tex_type,
+                            m_glesMajorVersion, m_glesMinorVersion,
+                            m_has_color_buffer_float_extension,
+                            m_has_color_buffer_half_float_extension);
+                    if (!renderable) {
+                        ALOGD("%s: tex not color renderable. format: 0x%x type 0x%x maj min %d %d floatext %d hfloatext %d\n", __func__, fbo_format_info.tex_internalformat, fbo_format_info.tex_type, m_glesMajorVersion, m_glesMinorVersion, m_has_color_buffer_float_extension, m_has_color_buffer_half_float_extension);
+                    }
+                    break;
+            }
+            break;
+        case FBO_ATTACHMENT_NONE:
+        default:
+            return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
+    }
+
+    if (!renderable) return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+
+    // Check dimensions
+    GLuint id;
+    switch (fbo_format_info.type) {
+    case FBO_ATTACHMENT_RENDERBUFFER:
+        id = getFboAttachmentRboId(target, attachment);
+        if (!fbo_format_info.rb_external) {
+            if (0 == queryRboWidth(id) || 0 == queryRboHeight(id)) {
+                ALOGD("%s: rbo has zero dimension\n", __func__);
+                return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+            }
+        }
+        break;
+    case FBO_ATTACHMENT_TEXTURE:
+        id = getFboAttachmentTextureId(target, attachment);
+        if (!fbo_format_info.tex_external) {
+            if (0 == queryTexWidth(fbo_format_info.tex_level, id) || 0 == queryTexHeight(fbo_format_info.tex_level, id)) {
+                ALOGD("%s: texture has zero dimension\n", __func__);
+                return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+            }
+            GLsizei depth = queryTexDepth(fbo_format_info.tex_level, id);
+            if (fbo_format_info.tex_layer >= depth) {
+                ALOGD("%s: texture layer/zoffset too high, wanted %d but only have %d layers\n", __func__,
+                      fbo_format_info.tex_layer, depth);
+                return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+            }
+        }
+        break;
+    case FBO_ATTACHMENT_NONE:
+    default:
+        return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
+    }
+
+    // Check samples
+    int currSamplesVal = *currentSamples;
+    bool firstTime = -1 == currSamplesVal;
+    int samplesThisAttachment = 0;
+    switch (fbo_format_info.type) {
+    case FBO_ATTACHMENT_RENDERBUFFER:
+        samplesThisAttachment = fbo_format_info.rb_multisamples;
+        break;
+    case FBO_ATTACHMENT_TEXTURE:
+        samplesThisAttachment = fbo_format_info.tex_multisamples;
+        break;
+    case FBO_ATTACHMENT_NONE:
+        break;
+    default:
+        return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
+    }
+
+    if (firstTime) {
+        *currentSamples = samplesThisAttachment;
+    } else {
+        if (samplesThisAttachment != currSamplesVal) {
+            return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
+        }
+    }
+
+    return 0;
+}
+
 // BEGIN driver workarounds-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
 // (>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')>
 
@@ -1396,6 +2007,10 @@
 
 void GLClientState::deleteTextures(GLsizei n, const GLuint* textures)
 {
+    for (const GLuint* texture = textures; texture != textures + n; texture++) {
+        setFboCompletenessDirtyForTexture(*texture);
+    }
+
     // Updating the textures array could be made more efficient when deleting
     // several textures:
     // - compacting the array could be done in a single pass once the deleted
@@ -1405,7 +2020,7 @@
     for (const GLuint* texture = textures; texture != textures + n; texture++) {
         texrec = getTextureRec(*texture);
         if (texrec && texrec->dims) {
-            delete texrec->dims;
+            delete [] texrec->dims;
         }
         if (texrec) {
             m_tex.textureRecs->erase(*texture);
@@ -1427,20 +2042,10 @@
 // RBO//////////////////////////////////////////////////////////////////////////
 
 void GLClientState::addFreshRenderbuffer(GLuint name) {
-    // if underlying opengl says these are fresh names,
-    // but we are keeping a stale one, reset it.
-    RboProps props;
-    props.target = GL_RENDERBUFFER;
-    props.name = name;
-    props.format = GL_NONE;
-    props.multisamples = 0;
-    props.previouslyBound = false;
+    if (!name) return;
 
-    if (usedRenderbufferName(name)) {
-        mRboState.rboData[getRboIndex(name)] = props;
-    } else {
-        mRboState.rboData.push_back(props);
-    }
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    view.addFresh(name);
 }
 
 void GLClientState::addRenderbuffers(GLsizei n, GLuint* renderbuffers) {
@@ -1449,84 +2054,87 @@
     }
 }
 
-size_t GLClientState::getRboIndex(GLuint name) const {
-    for (size_t i = 0; i < mRboState.rboData.size(); i++) {
-        if (mRboState.rboData[i].name == name) {
-            return i;
-        }
-    }
-    return -1;
-}
-
 void GLClientState::removeRenderbuffers(GLsizei n, const GLuint* renderbuffers) {
-    size_t bound_rbo_idx = getRboIndex(boundRboProps_const().name);
-
     std::vector<GLuint> to_remove;
     for (size_t i = 0; i < n; i++) {
         if (renderbuffers[i] != 0) { // Never remove the zero rb.
-            to_remove.push_back(getRboIndex(renderbuffers[i]));
+            to_remove.push_back(renderbuffers[i]);
+            setFboCompletenessDirtyForRbo(renderbuffers[i]);
         }
     }
 
-    for (size_t i = 0; i < to_remove.size(); i++) {
-        mRboState.rboData[to_remove[i]] = mRboState.rboData.back();
-        mRboState.rboData.pop_back();
+    bool unbindCurrent = false;
+    {
+        RenderbufferInfo::ScopedView view(mRboState.rboData);
+        for (size_t i = 0; i < to_remove.size(); i++) {
+            view.unref(to_remove[i]);
+        }
+        for (size_t i = 0; i < to_remove.size(); i++) {
+            if (mRboState.boundRenderbuffer == renderbuffers[i]) {
+                unbindCurrent = true;
+                break;
+            }
+        }
     }
 
-    // If we just deleted the currently bound rb,
-    // bind the zero rb
-    if (getRboIndex(boundRboProps_const().name) != bound_rbo_idx) {
+    if (unbindCurrent) {
         bindRenderbuffer(GL_RENDERBUFFER, 0);
     }
 }
 
 bool GLClientState::usedRenderbufferName(GLuint name) const {
-    for (size_t i = 0; i < mRboState.rboData.size(); i++) {
-        if (mRboState.rboData[i].name == name) {
-            return true;
-        }
-    }
-    return false;
-}
+    if (!name) return false;
 
-void GLClientState::setBoundRenderbufferIndex() {
-    for (size_t i = 0; i < mRboState.rboData.size(); i++) {
-        if (mRboState.rboData[i].name == mRboState.boundRenderbuffer) {
-            mRboState.boundRenderbufferIndex = i;
-            break;
-        }
-    }
-}
-
-RboProps& GLClientState::boundRboProps() {
-    return mRboState.rboData[mRboState.boundRenderbufferIndex];
-}
-
-const RboProps& GLClientState::boundRboProps_const() const {
-    return mRboState.rboData[mRboState.boundRenderbufferIndex];
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    return view.get_const(name) != 0;
 }
 
 void GLClientState::bindRenderbuffer(GLenum target, GLuint name) {
-    // If unused, add it.
-    if (!usedRenderbufferName(name)) {
-        addFreshRenderbuffer(name);
+
+    (void)target; // Must be GL_RENDERBUFFER
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    if (name != mRboState.boundRenderbuffer) {
+        view.unref(mRboState.boundRenderbuffer);
     }
+
     mRboState.boundRenderbuffer = name;
-    setBoundRenderbufferIndex();
-    boundRboProps().target = target;
-    boundRboProps().previouslyBound = true;
+
+    if (!name) return;
+
+    view.bind(name);
 }
 
 GLuint GLClientState::boundRenderbuffer() const {
-    return boundRboProps_const().name;
+    return mRboState.boundRenderbuffer;
 }
 
 void GLClientState::setBoundRenderbufferFormat(GLenum format) {
-    boundRboProps().format = format;
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    RboProps* props = view.get(mRboState.boundRenderbuffer);
+    if (!props) return;
+    props->format = format;
 }
 
 void GLClientState::setBoundRenderbufferSamples(GLsizei samples) {
-    boundRboProps().multisamples = samples;
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    RboProps* props = view.get(mRboState.boundRenderbuffer);
+    if (!props) return;
+    props->multisamples = samples;
+}
+
+void GLClientState::setBoundRenderbufferDimensions(GLsizei width, GLsizei height) {
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    RboProps* props = view.get(mRboState.boundRenderbuffer);
+    if (!props) return;
+    props->width = width;
+    props->height = height;
+}
+
+void GLClientState::setBoundRenderbufferEGLImageBacked() {
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    RboProps* props = view.get(mRboState.boundRenderbuffer);
+    if (!props) return;
+    props->boundEGLImage = true;
 }
 
 // FBO//////////////////////////////////////////////////////////////////////////
@@ -1534,11 +2142,38 @@
 // Format querying
 
 GLenum GLClientState::queryRboFormat(GLuint rbo_name) const {
-    return mRboState.rboData[getRboIndex(rbo_name)].format;
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    const RboProps* props = view.get(rbo_name);
+    if (!props) return 0;
+    return props->format;
 }
 
 GLsizei GLClientState::queryRboSamples(GLuint rbo_name) const {
-    return mRboState.rboData[getRboIndex(rbo_name)].multisamples;
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    const RboProps* props = view.get(rbo_name);
+    if (!props) return 0;
+    return props->multisamples;
+}
+
+GLsizei GLClientState::queryRboWidth(GLuint rbo_name) const {
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    const RboProps* props = view.get(rbo_name);
+    if (!props) return 0;
+    return props->width;
+}
+
+GLsizei GLClientState::queryRboHeight(GLuint rbo_name) const {
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    const RboProps* props = view.get(rbo_name);
+    if (!props) return 0;
+    return props->height;
+}
+
+bool GLClientState::queryRboEGLImageBacked(GLuint rbo_name) const {
+    RenderbufferInfo::ScopedView view(mRboState.rboData);
+    const RboProps* props = view.get(rbo_name);
+    if (!props) return 0;
+    return props->boundEGLImage;
 }
 
 GLint GLClientState::queryTexInternalFormat(GLuint tex_name) const {
@@ -1605,10 +2240,12 @@
     res_info->type = FBO_ATTACHMENT_NONE;
     res_info->rb_format = GL_NONE;
     res_info->rb_multisamples = 0;
+    res_info->rb_external = false;
     res_info->tex_internalformat = -1;
     res_info->tex_format = GL_NONE;
     res_info->tex_type = GL_NONE;
     res_info->tex_multisamples = 0;
+    res_info->tex_external = false;
 
     int colorAttachmentIndex =
         glUtilsColorAttachmentIndex(attachment);
@@ -1622,8 +2259,13 @@
             res_info->rb_multisamples =
                 queryRboSamples(
                         props.colorAttachmenti_rbos[colorAttachmentIndex]);
+            res_info->rb_external =
+                queryRboEGLImageBacked(
+                        props.colorAttachmenti_rbos[colorAttachmentIndex]);
         } else if (props.colorAttachmenti_hasTex[colorAttachmentIndex]) {
             res_info->type = FBO_ATTACHMENT_TEXTURE;
+            res_info->tex_external = queryTexEGLImageBacked(
+                    props.colorAttachmenti_textures[colorAttachmentIndex]);
             res_info->tex_internalformat =
                 queryTexInternalFormat(
                         props.colorAttachmenti_textures[colorAttachmentIndex]);
@@ -1634,6 +2276,8 @@
                 queryTexType(props.colorAttachmenti_textures[colorAttachmentIndex]);
             res_info->tex_multisamples =
                 queryTexSamples(props.colorAttachmenti_textures[colorAttachmentIndex]);
+            res_info->tex_level = props.colorAttachmenti_texture_levels[colorAttachmentIndex];
+            res_info->tex_layer = props.colorAttachmenti_texture_layers[colorAttachmentIndex];
         } else {
             res_info->type = FBO_ATTACHMENT_NONE;
         }
@@ -1647,13 +2291,19 @@
             res_info->rb_multisamples =
                 queryRboSamples(
                         props.depthAttachment_rbo);
+            res_info->rb_external =
+                queryRboEGLImageBacked(
+                        props.depthAttachment_rbo);
         } else if (props.depthAttachment_hasTexObj) {
             res_info->type = FBO_ATTACHMENT_TEXTURE;
+            res_info->tex_external = queryTexEGLImageBacked(props.depthAttachment_texture);
             res_info->tex_internalformat = queryTexInternalFormat(props.depthAttachment_texture);
             res_info->tex_format = queryTexFormat(props.depthAttachment_texture);
             res_info->tex_type = queryTexType(props.depthAttachment_texture);
             res_info->tex_multisamples =
                 queryTexSamples(props.depthAttachment_texture);
+            res_info->tex_level = props.depthAttachment_texture_level;
+            res_info->tex_layer = props.depthAttachment_texture_layer;
         } else {
             res_info->type = FBO_ATTACHMENT_NONE;
         }
@@ -1665,13 +2315,19 @@
             res_info->rb_multisamples =
                 queryRboSamples(
                         props.stencilAttachment_rbo);
+            res_info->rb_external =
+                queryRboEGLImageBacked(
+                        props.stencilAttachment_rbo);
         } else if (props.stencilAttachment_hasTexObj) {
             res_info->type = FBO_ATTACHMENT_TEXTURE;
+            res_info->tex_external = queryTexEGLImageBacked(props.stencilAttachment_texture);
             res_info->tex_internalformat = queryTexInternalFormat(props.stencilAttachment_texture);
             res_info->tex_format = queryTexFormat(props.stencilAttachment_texture);
             res_info->tex_type = queryTexType(props.stencilAttachment_texture);
             res_info->tex_multisamples =
                 queryTexSamples(props.stencilAttachment_texture);
+            res_info->tex_level = props.depthAttachment_texture_level;
+            res_info->tex_layer = props.depthAttachment_texture_layer;
         } else {
             res_info->type = FBO_ATTACHMENT_NONE;
         }
@@ -1683,13 +2339,19 @@
             res_info->rb_multisamples =
                 queryRboSamples(
                         props.depthstencilAttachment_rbo);
+            res_info->rb_external =
+                queryRboEGLImageBacked(
+                        props.depthstencilAttachment_rbo);
         } else if (props.depthstencilAttachment_hasTexObj) {
             res_info->type = FBO_ATTACHMENT_TEXTURE;
+            res_info->tex_external = queryTexEGLImageBacked(props.depthstencilAttachment_texture);
             res_info->tex_internalformat = queryTexInternalFormat(props.depthstencilAttachment_texture);
             res_info->tex_format = queryTexFormat(props.depthstencilAttachment_texture);
             res_info->tex_type = queryTexType(props.depthstencilAttachment_texture);
             res_info->tex_multisamples =
                 queryTexSamples(props.depthstencilAttachment_texture);
+            res_info->tex_level = props.depthAttachment_texture_level;
+            res_info->tex_layer = props.depthAttachment_texture_layer;
         } else {
             res_info->type = FBO_ATTACHMENT_NONE;
         }
@@ -1703,13 +2365,45 @@
     return info.type;
 }
 
-
 int GLClientState::getMaxColorAttachments() const {
-    return m_max_color_attachments;
+    return m_hostDriverCaps.max_color_attachments;
 }
 
 int GLClientState::getMaxDrawBuffers() const {
-    return m_max_draw_buffers;
+    return m_hostDriverCaps.max_draw_buffers;
+}
+
+#define UNIFORM_VALIDATION_ERR_COND(cond, code) if (cond) { *err = code; return; }
+
+#define UNIFORM_VALIDATION_INFO_VAR_NAME info
+
+#define UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_FLOATS \
+    (!(UNIFORM_VALIDATION_INFO_VAR_NAME->isBool) && (UNIFORM_VALIDATION_INFO_VAR_NAME->isInt || UNIFORM_VALIDATION_INFO_VAR_NAME->isSampler))
+
+#define UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_INTS \
+    (!(UNIFORM_VALIDATION_INFO_VAR_NAME->isBool) && (!UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_FLOATS || UNIFORM_VALIDATION_INFO_VAR_NAME->isUnsigned))
+
+#define UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_UNSIGNED_INTS \
+    (!(UNIFORM_VALIDATION_INFO_VAR_NAME->isBool) && (!UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_FLOATS || !(UNIFORM_VALIDATION_INFO_VAR_NAME->isUnsigned)))
+
+#define UNIFORM_VALIDATION_INLINING
+
+void GLClientState::validateUniform(bool isFloat, bool isUnsigned, GLint columns, GLint rows, GLint location, GLsizei count, GLenum* err) {
+    UNIFORM_VALIDATION_ERR_COND(!m_currentProgram && !m_currentShaderProgram, GL_INVALID_OPERATION);
+    if (-1 == location) return; \
+    auto info = currentUniformValidationInfo.get_const(location); \
+    UNIFORM_VALIDATION_ERR_COND(!info || !info->valid, GL_INVALID_OPERATION); \
+    UNIFORM_VALIDATION_ERR_COND(columns != info->columns || rows != info->rows, GL_INVALID_OPERATION); \
+    UNIFORM_VALIDATION_ERR_COND(count > 1 && !info->isArray, GL_INVALID_OPERATION);
+    if (isFloat) {
+        UNIFORM_VALIDATION_ERR_COND(UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_FLOATS, GL_INVALID_OPERATION);
+    } else {
+        if (isUnsigned) {
+            UNIFORM_VALIDATION_ERR_COND(UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_UNSIGNED_INTS, GL_INVALID_OPERATION);
+        } else {
+            UNIFORM_VALIDATION_ERR_COND(UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_INTS, GL_INVALID_OPERATION);
+        }
+    }
 }
 
 void GLClientState::addFreshFramebuffer(GLuint name) {
@@ -1717,22 +2411,32 @@
     props.name = name;
     props.previouslyBound = false;
 
-    props.colorAttachmenti_textures.resize(m_max_color_attachments, 0);
+    props.completenessDirty = true;
+
+    props.colorAttachmenti_textures.resize(m_hostDriverCaps.max_color_attachments, 0);
+    props.colorAttachmenti_texture_levels.resize(m_hostDriverCaps.max_color_attachments, 0);
+    props.colorAttachmenti_texture_layers.resize(m_hostDriverCaps.max_color_attachments, 0);
+
+    props.depthAttachment_texture_level = 0;
+    props.depthAttachment_texture_layer = 0;
+    props.stencilAttachment_texture_level = 0;
+    props.stencilAttachment_texture_layer = 0;
+
     props.depthAttachment_texture = 0;
     props.stencilAttachment_texture = 0;
     props.depthstencilAttachment_texture = 0;
 
-    props.colorAttachmenti_hasTex.resize(m_max_color_attachments, false);
+    props.colorAttachmenti_hasTex.resize(m_hostDriverCaps.max_color_attachments, false);
     props.depthAttachment_hasTexObj = false;
     props.stencilAttachment_hasTexObj = false;
     props.depthstencilAttachment_hasTexObj = false;
 
-    props.colorAttachmenti_rbos.resize(m_max_color_attachments, 0);
+    props.colorAttachmenti_rbos.resize(m_hostDriverCaps.max_color_attachments, 0);
     props.depthAttachment_rbo = 0;
     props.stencilAttachment_rbo = 0;
     props.depthstencilAttachment_rbo = 0;
 
-    props.colorAttachmenti_hasRbo.resize(m_max_color_attachments, false);
+    props.colorAttachmenti_hasRbo.resize(m_hostDriverCaps.max_color_attachments, false);
     props.depthAttachment_hasRbo = false;
     props.stencilAttachment_hasRbo = false;
     props.depthstencilAttachment_hasRbo = false;
@@ -1841,32 +2545,46 @@
 
 void GLClientState::attachTextureObject(
         GLenum target,
-        GLenum attachment, GLuint texture) {
+        GLenum attachment, GLuint texture, GLint level, GLint layer) {
+
+    bool attach = texture != 0;
 
     int colorAttachmentIndex =
         glUtilsColorAttachmentIndex(attachment);
 
+    boundFboProps(target).completenessDirty = true;
+
     if (colorAttachmentIndex != -1) {
         boundFboProps(target).colorAttachmenti_textures[colorAttachmentIndex] = texture;
-        boundFboProps(target).colorAttachmenti_hasTex[colorAttachmentIndex] = true;
+        boundFboProps(target).colorAttachmenti_texture_levels[colorAttachmentIndex] = level;
+        boundFboProps(target).colorAttachmenti_texture_layers[colorAttachmentIndex] = layer;
+        boundFboProps(target).colorAttachmenti_hasTex[colorAttachmentIndex] = attach;
     }
 
     switch (attachment) {
     case GL_DEPTH_ATTACHMENT:
         boundFboProps(target).depthAttachment_texture = texture;
-        boundFboProps(target).depthAttachment_hasTexObj = true;
+        boundFboProps(target).depthAttachment_texture_level = level;
+        boundFboProps(target).depthAttachment_texture_layer = layer;
+        boundFboProps(target).depthAttachment_hasTexObj = attach;
         break;
     case GL_STENCIL_ATTACHMENT:
         boundFboProps(target).stencilAttachment_texture = texture;
-        boundFboProps(target).stencilAttachment_hasTexObj = true;
+        boundFboProps(target).stencilAttachment_texture_level = level;
+        boundFboProps(target).stencilAttachment_texture_layer = layer;
+        boundFboProps(target).stencilAttachment_hasTexObj = attach;
         break;
     case GL_DEPTH_STENCIL_ATTACHMENT:
         boundFboProps(target).depthstencilAttachment_texture = texture;
-        boundFboProps(target).depthstencilAttachment_hasTexObj = true;
+        boundFboProps(target).depthstencilAttachment_hasTexObj = attach;
         boundFboProps(target).stencilAttachment_texture = texture;
-        boundFboProps(target).stencilAttachment_hasTexObj = true;
+        boundFboProps(target).stencilAttachment_hasTexObj = attach;
         boundFboProps(target).depthAttachment_texture = texture;
-        boundFboProps(target).depthAttachment_hasTexObj = true;
+        boundFboProps(target).depthAttachment_hasTexObj = attach;
+        boundFboProps(target).depthAttachment_texture_level = level;
+        boundFboProps(target).depthAttachment_texture_layer = layer;
+        boundFboProps(target).stencilAttachment_texture_level = level;
+        boundFboProps(target).stencilAttachment_texture_layer = layer;
         break;
     }
 }
@@ -1898,7 +2616,7 @@
 // RBOs for FBOs////////////////////////////////////////////////////////////////
 
 void GLClientState::detachRbo(GLuint renderbuffer) {
-    for (int i = 0; i < m_max_color_attachments; i++) {
+    for (int i = 0; i < m_hostDriverCaps.max_color_attachments; i++) {
         detachRboFromFbo(GL_DRAW_FRAMEBUFFER, glUtilsColorAttachmentName(i), renderbuffer);
         detachRboFromFbo(GL_READ_FRAMEBUFFER, glUtilsColorAttachmentName(i), renderbuffer);
     }
@@ -1917,6 +2635,8 @@
     int colorAttachmentIndex =
         glUtilsColorAttachmentIndex(attachment);
 
+    boundFboProps(target).completenessDirty = true;
+
     if (colorAttachmentIndex != -1) {
         if (boundFboProps(target).colorAttachmenti_hasRbo[colorAttachmentIndex] &&
             boundFboProps(target).colorAttachmenti_rbos[colorAttachmentIndex] == renderbuffer) {
@@ -1962,30 +2682,34 @@
 
 void GLClientState::attachRbo(GLenum target, GLenum attachment, GLuint renderbuffer) {
 
+    bool attach = 0 != renderbuffer;
+
     int colorAttachmentIndex =
         glUtilsColorAttachmentIndex(attachment);
 
+    boundFboProps(target).completenessDirty = true;
+
     if (colorAttachmentIndex != -1) {
         boundFboProps(target).colorAttachmenti_rbos[colorAttachmentIndex] = renderbuffer;
-        boundFboProps(target).colorAttachmenti_hasRbo[colorAttachmentIndex] = true;
+        boundFboProps(target).colorAttachmenti_hasRbo[colorAttachmentIndex] = attach;
     }
 
     switch (attachment) {
     case GL_DEPTH_ATTACHMENT:
         boundFboProps(target).depthAttachment_rbo = renderbuffer;
-        boundFboProps(target).depthAttachment_hasRbo = true;
+        boundFboProps(target).depthAttachment_hasRbo = attach;
         break;
     case GL_STENCIL_ATTACHMENT:
         boundFboProps(target).stencilAttachment_rbo = renderbuffer;
-        boundFboProps(target).stencilAttachment_hasRbo = true;
+        boundFboProps(target).stencilAttachment_hasRbo = attach;
         break;
     case GL_DEPTH_STENCIL_ATTACHMENT:
         boundFboProps(target).depthAttachment_rbo = renderbuffer;
-        boundFboProps(target).depthAttachment_hasRbo = true;
+        boundFboProps(target).depthAttachment_hasRbo = attach;
         boundFboProps(target).stencilAttachment_rbo = renderbuffer;
-        boundFboProps(target).stencilAttachment_hasRbo = true;
+        boundFboProps(target).stencilAttachment_hasRbo = attach;
         boundFboProps(target).depthstencilAttachment_rbo = renderbuffer;
-        boundFboProps(target).depthstencilAttachment_hasRbo = true;
+        boundFboProps(target).depthstencilAttachment_hasRbo = attach;
         break;
     }
 }
@@ -2014,6 +2738,80 @@
     return res;
 }
 
+void GLClientState::setFboCompletenessDirtyForTexture(GLuint texture) {
+    std::map<GLuint, FboProps>::iterator it = mFboState.fboData.begin();
+    while (it != mFboState.fboData.end()) {
+        FboProps& props = it->second;
+        for (int i = 0; i < m_hostDriverCaps.max_color_attachments; ++i) {
+            if (props.colorAttachmenti_hasTex[i]) {
+                if (texture == props.colorAttachmenti_textures[i]) {
+                    props.completenessDirty = true;
+                    return;
+                }
+            }
+        }
+
+        if (props.depthAttachment_hasTexObj) {
+            if (texture == props.depthAttachment_texture) {
+                    props.completenessDirty = true;
+                    return;
+            }
+        }
+
+        if (props.stencilAttachment_hasTexObj) {
+            if (texture == props.stencilAttachment_texture) {
+                props.completenessDirty = true;
+                return;
+            }
+        }
+
+        if (props.depthstencilAttachment_hasTexObj) {
+            if (texture == props.depthstencilAttachment_texture) {
+                props.completenessDirty = true;
+                return;
+            }
+        }
+        ++it;
+    }
+}
+
+void GLClientState::setFboCompletenessDirtyForRbo(GLuint rbo) {
+    std::map<GLuint, FboProps>::iterator it = mFboState.fboData.begin();
+    while (it != mFboState.fboData.end()) {
+        FboProps& props = it->second;
+        for (int i = 0; i < m_hostDriverCaps.max_color_attachments; ++i) {
+            if (props.colorAttachmenti_hasTex[i]) {
+                if (rbo == props.colorAttachmenti_rbos[i]) {
+                    props.completenessDirty = true;
+                    return;
+                }
+            }
+        }
+
+        if (props.depthAttachment_hasTexObj) {
+            if (rbo == props.depthAttachment_rbo) {
+                    props.completenessDirty = true;
+                    return;
+            }
+        }
+
+        if (props.stencilAttachment_hasTexObj) {
+            if (rbo == props.stencilAttachment_rbo) {
+                props.completenessDirty = true;
+                return;
+            }
+        }
+
+        if (props.depthstencilAttachment_hasRbo) {
+            if (rbo == props.depthstencilAttachment_rbo) {
+                props.completenessDirty = true;
+                return;
+            }
+        }
+        ++it;
+    }
+}
+
 bool GLClientState::attachmentHasObject(GLenum target, GLenum attachment) const {
     bool res = true; // liberal
 
@@ -2089,22 +2887,63 @@
     return 0;
 }
 
-void GLClientState::setTransformFeedbackActiveUnpaused(bool activeUnpaused) {
-    m_transformFeedbackActiveUnpaused = activeUnpaused;
+void GLClientState::setTransformFeedbackActive(bool active) {
+    m_transformFeedbackActive = active;
+}
+
+void GLClientState::setTransformFeedbackUnpaused(bool unpaused) {
+    m_transformFeedbackUnpaused = unpaused;
+}
+
+void GLClientState::setTransformFeedbackVaryingsCountForLinking(uint32_t count) {
+    m_transformFeedbackVaryingsCountForLinking = count;
+}
+
+bool GLClientState::getTransformFeedbackActive() const {
+    return m_transformFeedbackActive;
+}
+
+bool GLClientState::getTransformFeedbackUnpaused() const {
+    return m_transformFeedbackUnpaused;
 }
 
 bool GLClientState::getTransformFeedbackActiveUnpaused() const {
-    return m_transformFeedbackActiveUnpaused;
+    return m_transformFeedbackActive && m_transformFeedbackUnpaused;
+}
+
+uint32_t GLClientState::getTransformFeedbackVaryingsCountForLinking() const {
+    return m_transformFeedbackVaryingsCountForLinking;
 }
 
 void GLClientState::setTextureData(SharedTextureDataMap* sharedTexData) {
     m_tex.textureRecs = sharedTexData;
 }
 
+void GLClientState::setRenderbufferInfo(RenderbufferInfo* rbInfo) {
+    mRboState.rboData = rbInfo;
+}
+
+void GLClientState::setSamplerInfo(SamplerInfo* samplerInfo) {
+    mSamplerInfo = samplerInfo;
+}
+
+bool GLClientState::compressedTexImageSizeCompatible(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize) {
+    bool error = false;
+    GLsizei compressedSize = GLESTextureUtils::getCompressedImageSize(internalformat, width, height, depth, &error);
+    if (error) return false;
+    return imageSize == compressedSize;
+}
+
 void GLClientState::fromMakeCurrent() {
     if (mFboState.fboData.find(0) == mFboState.fboData.end()) {
         addFreshFramebuffer(0);
     }
+
+    if (!samplerExists(0)) {
+        GLuint id = 0;
+        setExistence(ObjectType::Sampler, true, 1, &id);
+    }
+
     FboProps& default_fb_props = mFboState.fboData[0];
     default_fb_props.colorAttachmenti_hasRbo[0] = true;
     default_fb_props.depthAttachment_hasRbo = true;
@@ -2113,30 +2952,30 @@
 }
 
 void GLClientState::initFromCaps(
-    int max_transform_feedback_separate_attribs,
-    int max_uniform_buffer_bindings,
-    int max_atomic_counter_buffer_bindings,
-    int max_shader_storage_buffer_bindings,
-    int max_vertex_attrib_bindings,
-    int max_color_attachments,
-    int max_draw_buffers) {
+    const HostDriverCaps& caps) {
+    m_hostDriverCaps = caps;
 
-    m_max_vertex_attrib_bindings = max_vertex_attrib_bindings;
+    // Override some of them
+    m_hostDriverCaps.max_vertex_attribs = CODEC_MAX_VERTEX_ATTRIBUTES;
+    m_hostDriverCaps.max_vertex_attrib_bindings = m_hostDriverCaps.max_vertex_attribs;
+
+    // Derive some other settings
+    m_log2MaxTextureSize = 0;
+    uint32_t current = 1;
+    while (current < m_hostDriverCaps.max_texture_size) {
+        current = current << 1;
+        ++m_log2MaxTextureSize;
+    }
 
     if (m_glesMajorVersion >= 3) {
-        m_max_transform_feedback_separate_attribs = max_transform_feedback_separate_attribs;
-        m_max_uniform_buffer_bindings = max_uniform_buffer_bindings;
-        m_max_atomic_counter_buffer_bindings = max_atomic_counter_buffer_bindings;
-        m_max_shader_storage_buffer_bindings = max_shader_storage_buffer_bindings;
-
-        if (m_max_transform_feedback_separate_attribs)
-            m_indexedTransformFeedbackBuffers.resize(m_max_transform_feedback_separate_attribs);
-        if (m_max_uniform_buffer_bindings)
-            m_indexedUniformBuffers.resize(m_max_uniform_buffer_bindings);
-        if (m_max_atomic_counter_buffer_bindings)
-            m_indexedAtomicCounterBuffers.resize(m_max_atomic_counter_buffer_bindings);
-        if (m_max_shader_storage_buffer_bindings)
-            m_indexedShaderStorageBuffers.resize(m_max_shader_storage_buffer_bindings);
+        if (m_hostDriverCaps.max_transform_feedback_separate_attribs)
+            m_indexedTransformFeedbackBuffers.resize(m_hostDriverCaps.max_transform_feedback_separate_attribs);
+        if (m_hostDriverCaps.max_uniform_buffer_bindings)
+            m_indexedUniformBuffers.resize(m_hostDriverCaps.max_uniform_buffer_bindings);
+        if (m_hostDriverCaps.max_atomic_counter_buffer_bindings)
+            m_indexedAtomicCounterBuffers.resize(m_hostDriverCaps.max_atomic_counter_buffer_bindings);
+        if (m_hostDriverCaps.max_shader_storage_buffer_bindings)
+            m_indexedShaderStorageBuffers.resize(m_hostDriverCaps.max_shader_storage_buffer_bindings);
 
         BufferBinding buf0Binding;
         buf0Binding.buffer = 0;
@@ -2155,10 +2994,6 @@
             m_indexedShaderStorageBuffers[i] = buf0Binding;
     }
 
-    m_max_color_attachments = max_color_attachments;
-    m_max_draw_buffers = max_draw_buffers;
-
-    addFreshRenderbuffer(0);
     addFreshFramebuffer(0);
 
     m_initialized = true;
@@ -2167,3 +3002,60 @@
 bool GLClientState::needsInitFromCaps() const {
     return !m_initialized;
 }
+
+void GLClientState::setExtensions(const std::string& extensions) {
+    if (!m_extensions_set) m_extensions = extensions;
+
+    m_has_color_buffer_float_extension =
+        hasExtension("GL_EXT_color_buffer_float");
+    m_has_color_buffer_half_float_extension =
+        hasExtension("GL_EXT_color_buffer_half_float");
+    m_extensions_set = true;
+}
+
+bool GLClientState::hasExtension(const char* ext) const {
+    return m_extensions.find(ext) != std::string::npos;
+}
+
+using android::base::guest::AutoLock;
+using android::base::guest::Lock;
+
+// A process-wide fence registry (because we can use fence sync objects across multiple contexts)
+struct FenceRegistry {
+    Lock lock;
+    PredicateMap<uint64_t, false> existence;
+
+    void onFenceCreated(GLsync sync) {
+        AutoLock scopedLock(lock);
+        uint64_t asUint64 = (uint64_t)(uintptr_t)(sync);
+        existence.add(asUint64);
+        existence.set(asUint64, true);
+    }
+
+    void onFenceDestroyed(GLsync sync) {
+        AutoLock scopedLock(lock);
+        uint64_t asUint64 = (uint64_t)(uintptr_t)(sync);
+        existence.remove(asUint64);
+    }
+
+    bool exists(GLsync sync) {
+        AutoLock scopedLock(lock);
+        uint64_t asUint64 = (uint64_t)(uintptr_t)(sync);
+        return existence.get(asUint64);
+    }
+};
+
+static FenceRegistry sFenceRegistry;
+
+void GLClientState::onFenceCreated(GLsync sync) {
+    sFenceRegistry.onFenceCreated(sync);
+}
+
+void GLClientState::onFenceDestroyed(GLsync sync) {
+    sFenceRegistry.onFenceDestroyed(sync);
+}
+
+bool GLClientState::fenceExists(GLsync sync) {
+    return sFenceRegistry.exists(sync);
+}
+
diff --git a/shared/OpenglCodecCommon/GLClientState.h b/shared/OpenglCodecCommon/GLClientState.h
index 1137041..c98190c 100644
--- a/shared/OpenglCodecCommon/GLClientState.h
+++ b/shared/OpenglCodecCommon/GLClientState.h
@@ -41,6 +41,36 @@
 #include <vector>
 #include <map>
 #include <set>
+#include <string>
+
+// Caps of host driver that make it easy to validate stuff
+struct HostDriverCaps {
+    // ES 2
+    int max_vertex_attribs;
+    int max_combined_texture_image_units;
+    int max_color_attachments;
+
+    int max_texture_size;
+    int max_texture_size_cube_map;
+    int max_renderbuffer_size;
+
+    // ES 3.0
+    int max_draw_buffers;
+
+    int ubo_offset_alignment;
+    int max_uniform_buffer_bindings;
+    int max_transform_feedback_separate_attribs;
+
+    int max_texture_size_3d;
+    int max_array_texture_layers;
+
+    // ES 3.1
+    int max_atomic_counter_buffer_bindings;
+    int max_shader_storage_buffer_bindings;
+    int max_vertex_attrib_bindings;
+    int max_vertex_attrib_stride;
+    int ssbo_offset_alignment;
+};
 
 // Tracking framebuffer objects:
 // which framebuffer is bound,
@@ -49,7 +79,17 @@
 struct FboProps {
     GLuint name;
     bool previouslyBound;
+    bool completenessDirty;
+    GLenum cachedCompleteness;
     std::vector<GLuint> colorAttachmenti_textures;
+    std::vector<GLint> colorAttachmenti_texture_levels;
+    std::vector<GLint> colorAttachmenti_texture_layers;
+
+    GLint depthAttachment_texture_level;
+    GLint depthAttachment_texture_layer;
+    GLint stencilAttachment_texture_level;
+    GLint stencilAttachment_texture_layer;
+
     GLuint depthAttachment_texture;
     GLuint stencilAttachment_texture;
     GLuint depthstencilAttachment_texture;
@@ -70,15 +110,6 @@
     bool depthstencilAttachment_hasRbo;
 };
 
-// Same for Rbo's
-struct RboProps {
-    GLenum target;
-    GLuint name;
-    GLenum format;
-    GLsizei multisamples;
-    bool previouslyBound;
-};
-
 // Enum for describing whether a framebuffer attachment
 // is a texture or renderbuffer.
 enum FboAttachmentType {
@@ -92,15 +123,27 @@
     FboAttachmentType type;
     GLenum rb_format;
     GLsizei rb_multisamples;
+    bool rb_external;
 
     GLint tex_internalformat;
     GLenum tex_format;
     GLenum tex_type;
     GLsizei tex_multisamples;
+    GLint tex_level;
+    GLint tex_layer;
+    bool tex_external;
 };
 
 class GLClientState {
 public:
+    // TODO: Unify everything in here
+    typedef enum {
+        Buffer,
+        TransformFeedback,
+        Sampler,
+        Query,
+    } ObjectType;
+
     typedef enum {
         VERTEX_LOCATION = 0,
         NORMAL_LOCATION = 1,
@@ -203,7 +246,7 @@
     GLClientState();
     GLClientState(int majorVersion, int minorVersion);
     ~GLClientState();
-    int nLocations() { return m_nLocations; }
+    int nLocations() { return CODEC_MAX_VERTEX_ATTRIBUTES; }
     const PixelStoreState *pixelStoreState() { return &m_pixelStore; }
     int setPixelStore(GLenum param, GLint value);
     GLuint currentVertexArrayObject() const { return m_currVaoState.vaoId(); }
@@ -234,10 +277,6 @@
     int getLocation(GLenum loc);
     void setActiveTexture(int texUnit) {m_activeTexture = texUnit; };
     int getActiveTexture() const { return m_activeTexture; }
-    void setMaxVertexAttribs(int val) {
-        m_maxVertexAttribs = val;
-        m_maxVertexAttribsDirty = false;
-    }
 
     void addBuffer(GLuint id);
     void removeBuffer(GLuint id);
@@ -247,6 +286,20 @@
     void setBufferHostMapDirty(GLuint id, bool dirty);
     bool isBufferHostMapDirty(GLuint id) const;
 
+    void setExistence(ObjectType type, bool exists, GLsizei count, const GLuint* ids);
+    bool queryExistence(ObjectType type, GLuint id) const;
+    bool samplerExists(GLuint id) const;
+    bool tryBind(GLenum target, GLuint id);
+    bool isBoundTargetValid(GLenum target);
+    bool isQueryBound(GLenum target);
+    bool isQueryObjectActive(GLuint id);
+    void setLastQueryTarget(GLenum target, GLuint id);
+    GLenum getLastQueryTarget(GLuint id);
+
+    static void onFenceCreated(GLsync sync);
+    static void onFenceDestroyed(GLsync sync);
+    static bool fenceExists(GLsync sync);
+
     void setBoundPixelPackBufferDirtyForHostMap();
     void setBoundTransformFeedbackBuffersDirtyForHostMap();
     void setBoundShaderStorageBuffersDirtyForHostMap();
@@ -258,6 +311,11 @@
     bool isNonIndexedBindNoOp(GLenum target, GLuint buffer);
     bool isIndexedBindNoOp(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size, GLintptr stride, GLintptr effectiveStride);
 
+    int getMaxTextureSize() const;
+    int getMaxTextureSize3D() const;
+    int getMaxTextureSizeCubeMap() const;
+    int getLog2MaxTextureSize() const;
+
     void postDraw();
     void postReadPixels();
     void postDispatchCompute();
@@ -332,7 +390,7 @@
     // glDisable(GL_TEXTURE_(2D|EXTERNAL_OES))
     void disableTextureTarget(GLenum target);
 
-    void bindSampler(GLuint unit, GLuint sampler);
+    bool bindSampler(GLuint unit, GLuint sampler);
     bool isSamplerBindNoOp(GLuint unit, GLuint sampler);
     void onDeleteSamplers(GLsizei n, const GLuint* samplers);
 
@@ -357,6 +415,15 @@
 
     // Return the texture currently bound to GL_TEXTURE_(2D|EXTERNAL_OES).
     GLuint getBoundTexture(GLenum target) const;
+    // Return bound framebuffer for target
+    GLuint getBoundFramebuffer(GLenum target) const;
+
+    // Check framebuffer completeness
+    GLenum checkFramebufferCompleteness(GLenum target);
+    // |currentSamples|: threads through the current sample count of attachments so far,
+    // for validating consistent number of samples across attachments
+    GLenum checkFramebufferAttachmentCompleteness(GLenum target, GLenum attachment, int* currentSamples) const;
+
     // Other publicly-visible texture queries
     GLenum queryTexLastBoundTarget(GLuint name) const;
     GLenum queryTexFormat(GLuint name) const;
@@ -383,12 +450,14 @@
     void setBoundTextureInternalFormat(GLenum target, GLint format);
     void setBoundTextureFormat(GLenum target, GLenum format);
     void setBoundTextureType(GLenum target, GLenum type);
-    void setBoundTextureDims(GLenum target, GLsizei level, GLsizei width, GLsizei height, GLsizei depth);
+    void setBoundTextureDims(GLenum target, GLenum cubetarget, GLsizei level, GLsizei width, GLsizei height, GLsizei depth);
     void setBoundTextureSamples(GLenum target, GLsizei samples);
+    void addTextureCubeMapImage(GLenum stateTarget, GLenum cubeTarget);
 
     // glTexStorage2D disallows any change in texture format after it is set for a particular texture.
     void setBoundTextureImmutableFormat(GLenum target);
     bool isBoundTextureImmutableFormat(GLenum target) const;
+    bool isBoundTextureComplete(GLenum target) const;
 
     // glDeleteTextures(...)
     // Remove references to the to-be-deleted textures.
@@ -402,6 +471,8 @@
     GLuint boundRenderbuffer() const;
     void setBoundRenderbufferFormat(GLenum format);
     void setBoundRenderbufferSamples(GLsizei samples);
+    void setBoundRenderbufferDimensions(GLsizei width, GLsizei height);
+    void setBoundRenderbufferEGLImageBacked();
 
     // Frame buffer objects
     void addFramebuffers(GLsizei n, GLuint* framebuffers);
@@ -413,7 +484,7 @@
     GLuint boundFramebuffer(GLenum target) const;
 
     // Texture object -> FBO
-    void attachTextureObject(GLenum target, GLenum attachment, GLuint texture);
+    void attachTextureObject(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
     GLuint getFboAttachmentTextureId(GLenum target, GLenum attachment) const;
 
     // RBO -> FBO
@@ -426,11 +497,24 @@
     bool attachmentHasObject(GLenum target, GLenum attachment) const;
     GLuint objectOfAttachment(GLenum target, GLenum attachment) const;
 
+    // Dirty FBO completeness
+    void setFboCompletenessDirtyForTexture(GLuint texture);
+    void setFboCompletenessDirtyForRbo(GLuint rbo_name);
+
     // Transform feedback state
-    void setTransformFeedbackActiveUnpaused(bool activeUnpaused);
+    void setTransformFeedbackActive(bool active);
+    void setTransformFeedbackUnpaused(bool unpaused);
+    void setTransformFeedbackVaryingsCountForLinking(uint32_t count);
+    bool getTransformFeedbackActive() const;
+    bool getTransformFeedbackUnpaused() const;
     bool getTransformFeedbackActiveUnpaused() const;
+    uint32_t getTransformFeedbackVaryingsCountForLinking() const;
 
     void setTextureData(SharedTextureDataMap* sharedTexData);
+    void setRenderbufferInfo(RenderbufferInfo* rbInfo);
+    void setSamplerInfo(SamplerInfo* samplerInfo);
+
+    bool compressedTexImageSizeCompatible(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize);
     // set eglsurface property on default framebuffer
     // if coming from eglMakeCurrent
     void fromMakeCurrent();
@@ -439,14 +523,10 @@
     // accurate values for indexed buffers
     // and # render targets.
     void initFromCaps(
-        int max_transform_feedback_separate_attribs,
-        int max_uniform_buffer_bindings,
-        int max_atomic_counter_buffer_bindings,
-        int max_shader_storage_buffer_bindings,
-        int max_vertex_attrib_bindings,
-        int max_color_attachments,
-        int max_draw_buffers);
+        const HostDriverCaps& caps);
     bool needsInitFromCaps() const;
+    void setExtensions(const std::string& extensions);
+    bool hasExtension(const char* ext) const;
 
     // Queries the format backing the current framebuffer.
     // Type differs depending on whether the attachment
@@ -460,14 +540,47 @@
             GLenum attachment) const;
     int getMaxColorAttachments() const;
     int getMaxDrawBuffers() const;
+
+    // Uniform validation info
+    UniformValidationInfo currentUniformValidationInfo;
+    // TODO: Program uniform validation info
+
+    // Uniform validation api
+    void validateUniform(bool isFloat, bool isUnsigned, GLint columns, GLint rows, GLint location, GLsizei count, GLenum* err);
+
 private:
     void init();
     bool m_initialized;
     PixelStoreState m_pixelStore;
 
 #ifdef GFXSTREAM
-    PredicateMap<false> mBufferIds;
-    PredicateMap<true> mHostMappedBufferDirty;
+    using DirtyMap = PredicateMap<uint32_t, true>;
+
+    ExistenceMap mBufferIds;
+    ExistenceMap mTransformFeedbackIds;
+    SamplerInfo* mSamplerInfo;
+    ExistenceMap mQueryIds;
+    LastQueryTargetInfo mLastQueryTargets;
+
+    // Bound query target validity and tracking
+    struct BoundTargetInfo {
+        GLuint id;
+        bool valid;
+    };
+   
+    // Transform feedback
+    BoundTargetInfo mBoundTransformFeedbackValidity;
+
+    // Queries
+    // GL_ANY_SAMPLES_PASSED
+    BoundTargetInfo mBoundQueryValidity_AnySamplesPassed;
+    // GL_ANY_SAMPLES_PASSED_CONSERVATIVE
+    BoundTargetInfo mBoundQueryValidity_AnySamplesPassedConservative;
+    // GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
+    BoundTargetInfo mBoundQueryValidity_TransformFeedbackPrimitivesWritten;
+
+    // Dirty maps
+    DirtyMap mHostMappedBufferDirty;
 #else
     std::set<GLuint> mBufferIds;
 #endif
@@ -499,23 +612,23 @@
     GLuint m_drawIndirectBuffer;
     GLuint m_shaderStorageBuffer;
 
-    bool m_transformFeedbackActiveUnpaused;
+    bool m_transformFeedbackActive;
+    bool m_transformFeedbackUnpaused;
+    uint32_t m_transformFeedbackVaryingsCountForLinking;
 
-    int m_max_transform_feedback_separate_attribs;
-    int m_max_uniform_buffer_bindings;
-    int m_max_atomic_counter_buffer_bindings;
-    int m_max_shader_storage_buffer_bindings;
-    int m_max_vertex_attrib_bindings;
+    HostDriverCaps m_hostDriverCaps;
+    bool m_extensions_set;
+    std::string m_extensions;
+    bool m_has_color_buffer_float_extension;
+    bool m_has_color_buffer_half_float_extension;
     std::vector<BufferBinding> m_indexedTransformFeedbackBuffers;
     std::vector<BufferBinding> m_indexedUniformBuffers;
     std::vector<BufferBinding> m_indexedAtomicCounterBuffers;
     std::vector<BufferBinding> m_indexedShaderStorageBuffers;
+    int m_log2MaxTextureSize;
 
     int m_glesMajorVersion;
     int m_glesMinorVersion;
-    int m_maxVertexAttribs;
-    bool m_maxVertexAttribsDirty;
-    int m_nLocations;
     int m_activeTexture;
     GLint m_currentProgram;
     GLint m_currentShaderProgram;
@@ -570,19 +683,14 @@
     GLenum copyTexImageNeededTarget(GLenum target, GLint level,
                                     GLenum internalformat);
 
-    int m_max_color_attachments;
-    int m_max_draw_buffers;
     struct RboState {
         GLuint boundRenderbuffer;
-        size_t boundRenderbufferIndex;
-        std::vector<RboProps> rboData;
+        // Connects to share group.
+        // Expected that share group lifetime outlives this context.
+        RenderbufferInfo* rboData;
     };
     RboState mRboState;
     void addFreshRenderbuffer(GLuint name);
-    void setBoundRenderbufferIndex();
-    size_t getRboIndex(GLuint name) const;
-    RboProps& boundRboProps();
-    const RboProps& boundRboProps_const() const;
 
     struct FboState {
         GLuint boundDrawFramebuffer;
@@ -600,6 +708,9 @@
     // Querying framebuffer format
     GLenum queryRboFormat(GLuint name) const;
     GLsizei queryRboSamples(GLuint name) const;
+    GLsizei queryRboWidth(GLuint name) const;
+    GLsizei queryRboHeight(GLuint name) const;
+    bool queryRboEGLImageBacked(GLuint name) const;
     GLenum queryTexType(GLuint name) const;
     GLsizei queryTexSamples(GLuint name) const;
 
@@ -608,6 +719,13 @@
     TextureRec* getTextureRec(GLuint id) const;
 
 public:
+    bool isTexture(GLuint name) const;
+    bool isTextureWithStorage(GLuint name) const;
+    bool isTextureWithTarget(GLuint name) const;
+    bool isTextureCubeMap(GLuint name) const;
+    bool isRenderbuffer(GLuint name) const;
+    bool isRenderbufferThatWasBound(GLuint name) const;
+
     void getClientStatePointer(GLenum pname, GLvoid** params);
 
     template <class T>
@@ -839,12 +957,8 @@
             break;
             }
         case GL_MAX_VERTEX_ATTRIBS: {
-            if (m_maxVertexAttribsDirty) {
-                isClientStateParam = false;
-            } else {
-                *out = m_maxVertexAttribs;
-                isClientStateParam = true;
-            }
+            *out = CODEC_MAX_VERTEX_ATTRIBUTES;
+            isClientStateParam = true;
             break;
         }
         }
diff --git a/shared/OpenglCodecCommon/GLESTextureUtils.cpp b/shared/OpenglCodecCommon/GLESTextureUtils.cpp
index cedcda8..8b4c0c2 100644
--- a/shared/OpenglCodecCommon/GLESTextureUtils.cpp
+++ b/shared/OpenglCodecCommon/GLESTextureUtils.cpp
@@ -1,6 +1,8 @@
 #include "GLESTextureUtils.h"
 
 #include "glUtils.h"
+#include "etc.h"
+#include "astc-codec.h"
 
 #if PLATFORM_SDK_VERSION < 26
 #include <cutils/log.h>
@@ -8,6 +10,36 @@
 #include <log/log.h>
 #endif
 
+#define ASTC_FORMATS_LIST(EXPAND_MACRO) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, astc_codec::FootprintType::k4x4, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_5x4_KHR, astc_codec::FootprintType::k5x4, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_5x5_KHR, astc_codec::FootprintType::k5x5, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_6x5_KHR, astc_codec::FootprintType::k6x5, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, astc_codec::FootprintType::k6x6, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_8x5_KHR, astc_codec::FootprintType::k8x5, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_8x6_KHR, astc_codec::FootprintType::k8x6, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, astc_codec::FootprintType::k8x8, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_10x5_KHR, astc_codec::FootprintType::k10x5, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_10x6_KHR, astc_codec::FootprintType::k10x6, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_10x8_KHR, astc_codec::FootprintType::k10x8, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, astc_codec::FootprintType::k10x10, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_12x10_KHR, astc_codec::FootprintType::k12x10, false) \
+    EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, astc_codec::FootprintType::k12x12, false) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, astc_codec::FootprintType::k4x4, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, astc_codec::FootprintType::k5x4, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, astc_codec::FootprintType::k5x5, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, astc_codec::FootprintType::k6x5, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, astc_codec::FootprintType::k6x6, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, astc_codec::FootprintType::k8x5, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, astc_codec::FootprintType::k8x6, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, astc_codec::FootprintType::k8x8, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, astc_codec::FootprintType::k10x5, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, astc_codec::FootprintType::k10x6, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, astc_codec::FootprintType::k10x8, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, astc_codec::FootprintType::k10x10, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, astc_codec::FootprintType::k12x10, true) \
+    EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, astc_codec::FootprintType::k12x12, true) \
+
 namespace GLESTextureUtils {
 
 // Based on computations in
@@ -332,6 +364,8 @@
         int* packingPixelImageSize,
         int* packingTotalImageSize) {
 
+    (void)depth;
+
     int widthTotal = (packRowLength == 0) ? width : packRowLength;
     int totalRowSize = computePitch(widthTotal, format, type, packAlignment);
     int pixelsOnlyRowSize = computePitch(width, format, type, packAlignment);
@@ -352,4 +386,184 @@
     if (packingTotalImageSize) *packingTotalImageSize = totalImageSize;
 }
 
+bool isEtcFormat(GLenum internalformat) {
+    switch (internalformat) {
+    case GL_ETC1_RGB8_OES:
+    case GL_COMPRESSED_RGB8_ETC2:
+    case GL_COMPRESSED_SRGB8_ETC2:
+    case GL_COMPRESSED_RGBA8_ETC2_EAC:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
+    case GL_COMPRESSED_R11_EAC:
+    case GL_COMPRESSED_SIGNED_R11_EAC:
+    case GL_COMPRESSED_RG11_EAC:
+    case GL_COMPRESSED_SIGNED_RG11_EAC:
+    case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+    case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+        return true;
+    }
+    return false;
+}
+
+bool isEtc2Format(GLenum internalformat) {
+    return internalformat != GL_ETC1_RGB8_OES &&
+        isEtcFormat(internalformat);
+}
+
+ETC2ImageFormat getEtcFormat(GLenum internalformat) {
+    ETC2ImageFormat etcFormat = EtcRGB8;
+    switch (internalformat) {
+        case GL_COMPRESSED_RGB8_ETC2:
+        case GL_ETC1_RGB8_OES:
+            break;
+        case GL_COMPRESSED_RGBA8_ETC2_EAC:
+            etcFormat = EtcRGBA8;
+            break;
+        case GL_COMPRESSED_SRGB8_ETC2:
+            break;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
+            etcFormat = EtcRGBA8;
+            break;
+        case GL_COMPRESSED_R11_EAC:
+            etcFormat = EtcR11;
+            break;
+        case GL_COMPRESSED_SIGNED_R11_EAC:
+            etcFormat = EtcSignedR11;
+            break;
+        case GL_COMPRESSED_RG11_EAC:
+            etcFormat = EtcRG11;
+            break;
+        case GL_COMPRESSED_SIGNED_RG11_EAC:
+            etcFormat = EtcSignedRG11;
+            break;
+        case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+            etcFormat = EtcRGB8A1;
+            break;
+        case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+            etcFormat = EtcRGB8A1;
+            break;
+    }
+    return etcFormat;
+}
+
+bool isAstcFormat(GLenum internalformat) {
+    switch (internalformat) {
+#define ASTC_FORMAT(typeName, footprintType, srgbValue) \
+        case typeName:
+
+        ASTC_FORMATS_LIST(ASTC_FORMAT)
+#undef ASTC_FORMAT
+            return true;
+        default:
+            return false;
+    }
+}
+
+void getAstcFormatInfo(GLenum internalformat,
+                       astc_codec::FootprintType* footprint,
+                       bool* srgb) {
+    switch (internalformat) {
+#define ASTC_FORMAT(typeName, footprintType, srgbValue) \
+        case typeName: \
+            *footprint = footprintType; *srgb = srgbValue; break; \
+
+        ASTC_FORMATS_LIST(ASTC_FORMAT)
+#undef ASTC_FORMAT
+        default:
+            ALOGE("%s: invalid astc format: 0x%x\n", __func__, internalformat);
+            abort();
+    }
+}
+
+int getAstcFootprintWidth(astc_codec::FootprintType footprint) {
+    switch (footprint) {
+        case astc_codec::FootprintType::k4x4: return 4;
+        case astc_codec::FootprintType::k5x4: return 5;
+        case astc_codec::FootprintType::k5x5: return 5;
+        case astc_codec::FootprintType::k6x5: return 6;
+        case astc_codec::FootprintType::k6x6: return 6;
+        case astc_codec::FootprintType::k8x5: return 8;
+        case astc_codec::FootprintType::k8x6: return 8;
+        case astc_codec::FootprintType::k10x5: return 10;
+        case astc_codec::FootprintType::k10x6: return 10;
+        case astc_codec::FootprintType::k8x8: return 8;
+        case astc_codec::FootprintType::k10x8: return 10;
+        case astc_codec::FootprintType::k10x10: return 10;
+        case astc_codec::FootprintType::k12x10: return 12;
+        case astc_codec::FootprintType::k12x12: return 12;
+        default:
+            ALOGE("%s: invalid astc footprint: 0x%x\n", __func__, footprint);
+            abort();
+    }
+}
+
+int getAstcFootprintHeight(astc_codec::FootprintType footprint) {
+    switch (footprint) {
+        case astc_codec::FootprintType::k4x4: return 4;
+        case astc_codec::FootprintType::k5x4: return 4;
+        case astc_codec::FootprintType::k5x5: return 5;
+        case astc_codec::FootprintType::k6x5: return 5;
+        case astc_codec::FootprintType::k6x6: return 6;
+        case astc_codec::FootprintType::k8x5: return 5;
+        case astc_codec::FootprintType::k8x6: return 6;
+        case astc_codec::FootprintType::k10x5: return 5;
+        case astc_codec::FootprintType::k10x6: return 6;
+        case astc_codec::FootprintType::k8x8: return 8;
+        case astc_codec::FootprintType::k10x8: return 8;
+        case astc_codec::FootprintType::k10x10: return 10;
+        case astc_codec::FootprintType::k12x10: return 10;
+        case astc_codec::FootprintType::k12x12: return 12;
+        default:
+            ALOGE("%s: invalid astc footprint: 0x%x\n", __func__, footprint);
+            abort();
+    }
+}
+
+GLsizei getAstcCompressedSize(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool* error) {
+    bool srgb;
+    astc_codec::FootprintType footprintType;
+    getAstcFormatInfo(internalformat, &footprintType, &srgb);
+
+    int fpWidth = getAstcFootprintWidth(footprintType);
+    int fpHeight = getAstcFootprintHeight(footprintType);
+
+    if (width == 0 || height == 0 || depth == 0) {
+        *error = true;
+        return 0;
+    }
+
+    const size_t blocks_wide = (width + fpWidth - 1) / fpWidth;
+    if (blocks_wide == 0) {
+        *error = true;
+        return 0;
+    }
+
+    const size_t expected_block_count =
+        ((width + fpWidth - 1) / fpWidth) *
+        ((height + fpHeight - 1) / fpHeight);
+
+    const size_t kPhysBlockSizeBytes = 16;
+
+    GLsizei res = kPhysBlockSizeBytes * expected_block_count * depth;
+
+    return res;
+}
+
+GLsizei getCompressedImageSize(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool* error) {
+    if (isEtcFormat(internalformat)) {
+        GLsizei total = 0;
+        GLsizei one = etc_get_encoded_data_size(getEtcFormat(internalformat), width, height);
+        for (GLsizei i = 0; i < depth; ++i) {
+            total += one;
+        }
+        return total;
+    }
+
+    if (isAstcFormat(internalformat)) {
+        return getAstcCompressedSize(internalformat, width, height, depth, error);
+    }
+
+    ALOGE("%s: Unknown compressed internal format: 0x%x\n", __func__, internalformat);
+    abort();
+}
+
 } // namespace GLESTextureUtils
diff --git a/shared/OpenglCodecCommon/GLESTextureUtils.h b/shared/OpenglCodecCommon/GLESTextureUtils.h
index 1d26b3a..c2948bd 100644
--- a/shared/OpenglCodecCommon/GLESTextureUtils.h
+++ b/shared/OpenglCodecCommon/GLESTextureUtils.h
@@ -71,5 +71,12 @@
         int* packingPixelImageSize,
         int* packingTotalImageSize);
 
+// For calculating compressed sizes of ETC/EAC formatted images in the guest.
+GLsizei getCompressedImageSize(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool* error);
+
+// Format queries
+bool isEtc2Format(GLenum internalformat);
+bool isAstcFormat(GLenum internalformat);
+
 } // namespace GLESTextureUtils
 #endif
diff --git a/shared/OpenglCodecCommon/GLSharedGroup.cpp b/shared/OpenglCodecCommon/GLSharedGroup.cpp
index 5273e4c..848dfdb 100755
--- a/shared/OpenglCodecCommon/GLSharedGroup.cpp
+++ b/shared/OpenglCodecCommon/GLSharedGroup.cpp
@@ -17,6 +17,7 @@
 #include "GLSharedGroup.h"
 
 #include "KeyedVectorUtils.h"
+#include "glUtils.h"
 
 /**** BufferData ****/
 
@@ -36,13 +37,19 @@
 
 /**** ProgramData ****/
 ProgramData::ProgramData() : m_numIndexes(0),
+                             m_numAttributes(0),
                              m_initialized(false) {
     m_Indexes = NULL;
+    m_refcount = 1;
+    m_linkStatus = 0;
+    m_activeUniformBlockCount = 0;
+    m_transformFeedbackVaryingsCount = 0;
 }
 
-void ProgramData::initProgramData(GLuint numIndexes) {
+void ProgramData::initProgramData(GLuint numIndexes, GLuint numAttributes) {
     m_initialized = true;
     m_numIndexes = numIndexes;
+    m_numAttributes = numAttributes;
 
     delete [] m_Indexes;
 
@@ -104,6 +111,16 @@
     return 0;
 }
 
+bool ProgramData::isValidUniformLocation(GLint location) {
+    for (GLuint i = 0; i < m_numIndexes; ++i) {
+        if (location >= m_Indexes[i].base &&
+            location < m_Indexes[i].base + m_Indexes[i].size)
+            return true;
+    }
+
+    return false;
+}
+
 GLint ProgramData::getNextSamplerUniform(
     GLint index, GLint* val, GLenum* target) {
 
@@ -154,15 +171,18 @@
     return false;
 }
 
-bool ProgramData::attachShader(GLuint shader) {
+bool ProgramData::attachShader(GLuint shader, GLenum shaderType) {
     size_t n = m_shaders.size();
 
     for (size_t i = 0; i < n; i++) {
         if (m_shaders[i] == shader) {
             return false;
+        } else if (m_shaderTypes[i] == shaderType) {
+            return false;
         }
     }
     m_shaders.push_back(shader);
+    m_shaderTypes.push_back(shaderType);
     return true;
 }
 
@@ -172,6 +192,7 @@
     for (size_t i = 0; i < n; i++) {
         if (m_shaders[i] == shader) {
             m_shaders.erase(m_shaders.begin() + i);
+            m_shaderTypes.erase(m_shaderTypes.begin() + i);
             return true;
         }
     }
@@ -179,6 +200,34 @@
     return false;
 }
 
+UniformValidationInfo ProgramData::compileValidationInfo(bool* error) const {
+    UniformValidationInfo res;
+    if (!m_Indexes) {
+        *error = true;
+        return res;
+    }
+
+    for (GLuint i = 0; i < m_numIndexes; ++i) {
+        if (m_Indexes[i].base < 0) continue;
+
+        UniformLocationInfo info = {
+            .valid = true,
+            .columns = getColumnsOfType(m_Indexes[i].type),
+            .rows = getRowsOfType(m_Indexes[i].type),
+            .isSampler = isSamplerType(m_Indexes[i].type),
+            .isInt = isIntegerType(m_Indexes[i].type),
+            .isArray = m_Indexes[i].size > 1,
+            .isUnsigned = isUnsignedIntType(m_Indexes[i].type),
+            .isBool = isBoolType(m_Indexes[i].type),
+        };
+        for (GLuint j = 0; j < m_Indexes[i].size; ++j) {
+            res.add(m_Indexes[i].base + j, info);
+        }
+    }
+
+    return res;
+}
+
 /***** GLSharedGroup ****/
 
 GLSharedGroup::GLSharedGroup() { }
@@ -212,6 +261,14 @@
     return &m_textureRecs;
 }
 
+RenderbufferInfo* GLSharedGroup::getRenderbufferInfo() {
+    return &m_renderbufferInfo;
+}
+
+SamplerInfo* GLSharedGroup::getSamplerInfo() {
+    return &m_samplerInfo;
+}
+
 void GLSharedGroup::addBufferData(GLuint bufferId, GLsizeiptr size, const void* data) {
 
     android::AutoMutex _lock(m_lock);
@@ -302,16 +359,37 @@
     m_programs[program] = new ProgramData();
 }
 
-void GLSharedGroup::initProgramData(GLuint program, GLuint numIndexes) {
+void GLSharedGroup::initProgramData(GLuint program, GLuint numIndexes, GLuint numAttributes) {
 
     android::AutoMutex _lock(m_lock);
 
     ProgramData* pData = findObjectOrDefault(m_programs, program);
     if (pData) {
-        pData->initProgramData(numIndexes);
+        pData->initProgramData(numIndexes, numAttributes);
     }
 }
 
+void GLSharedGroup::refProgramData(GLuint program) {
+    android::AutoMutex _lock(m_lock);
+    ProgramData* pData = findObjectOrDefault(m_programs, program);
+    if (!pData) return;
+    pData->incRef();
+}
+
+void GLSharedGroup::onUseProgram(GLuint previous, GLuint next) {
+    if (previous == next) return;
+
+    android::AutoMutex _lock(m_lock);
+
+    if (previous) {
+        deleteProgramDataLocked(previous);
+    }
+
+    ProgramData* pData = findObjectOrDefault(m_programs, next);
+    if (!pData) return;
+    pData->incRef();
+}
+
 bool GLSharedGroup::isProgramInitialized(GLuint program) {
 
     android::AutoMutex _lock(m_lock);
@@ -337,14 +415,23 @@
 }
 
 void GLSharedGroup::deleteProgramData(GLuint program) {
-
     android::AutoMutex _lock(m_lock);
+    deleteProgramDataLocked(program);
+}
+
+void GLSharedGroup::deleteProgramDataLocked(GLuint program) {
 
     ProgramData* pData = findObjectOrDefault(m_programs, program);
 
-    if (pData) delete pData;
-
-    m_programs.erase(program);
+    if (pData && pData->decRef()) {
+        size_t numShaders = pData->getNumShaders();
+        for (size_t i = 0; i < numShaders; ++i) {
+            // changes the first one
+            detachShaderLocked(program, pData->getShader(0));
+        }
+        delete pData;
+        m_programs.erase(program);
+    }
 
     if (m_shaderProgramIdMap.find(program) ==
         m_shaderProgramIdMap.end()) return;
@@ -360,31 +447,43 @@
 }
 
 // No such thing for separable shader programs.
-void GLSharedGroup::attachShader(GLuint program, GLuint shader) {
-
+bool GLSharedGroup::attachShader(GLuint program, GLuint shader) {
     android::AutoMutex _lock(m_lock);
 
     ProgramData* pData = findObjectOrDefault(m_programs, program);
     ShaderData* sData = findObjectOrDefault(m_shaders, shader);
 
+    bool res = false;
+
     if (pData && sData) {
-        if (pData->attachShader(shader)) {
+        res = pData->attachShader(shader, sData->shaderType);
+        if (res) {
             refShaderDataLocked(shader);
         }
     }
+
+    return res;
 }
 
-void GLSharedGroup::detachShader(GLuint program, GLuint shader) {
-
+bool GLSharedGroup::detachShader(GLuint program, GLuint shader) {
     android::AutoMutex _lock(m_lock);
+    return detachShaderLocked(program, shader);
+}
 
+bool GLSharedGroup::detachShaderLocked(GLuint program, GLuint shader) {
     ProgramData* pData = findObjectOrDefault(m_programs, program);
     ShaderData* sData = findObjectOrDefault(m_shaders, shader);
+
+    bool res = false;
+
     if (pData && sData) {
-        if (pData->detachShader(shader)) {
+        res = pData->detachShader(shader);
+        if (res) {
             unrefShaderDataLocked(shader);
         }
     }
+
+    return res;
 }
 
 // Not needed/used for separate shader programs.
@@ -509,6 +608,19 @@
     return false;
 }
 
+bool GLSharedGroup::isProgramUniformLocationValid(GLuint program, GLint location) {
+    if (location < 0) return false;
+
+    android::AutoMutex _lock(m_lock);
+
+    ProgramData* pData =
+        findObjectOrDefault(m_programs, program);
+
+    if (!pData) return false;
+
+    return pData->isValidUniformLocation(location);
+}
+
 bool GLSharedGroup::isShader(GLuint shader) {
 
     android::AutoMutex _lock(m_lock);
@@ -518,7 +630,7 @@
     return pData != NULL;
 }
 
-bool GLSharedGroup::addShaderData(GLuint shader) {
+bool GLSharedGroup::addShaderData(GLuint shader, GLenum shaderType) {
 
     android::AutoMutex _lock(m_lock);
 
@@ -527,6 +639,7 @@
     if (data) {
         m_shaders[shader] = data;
         data->refcount = 1;
+        data->shaderType = shaderType;
     }
 
     return data != NULL;
@@ -562,6 +675,21 @@
     }
 }
 
+ProgramData* GLSharedGroup::getProgramDataLocked(GLuint program) {
+    // Check the space of normal programs, then separable ones
+    ProgramData* pData = findObjectOrDefault(m_programs, program);
+
+    if (pData) return pData;
+
+    std::map<GLuint, uint32_t>::const_iterator it =
+        m_shaderProgramIdMap.find(program);
+    if (it == m_shaderProgramIdMap.end()) return NULL;
+
+    ShaderProgramData* spData = findObjectOrDefault(m_shaderPrograms, it->second);
+    if (!spData) return NULL;
+    return &spData->programData;
+}
+
 uint32_t GLSharedGroup::addNewShaderProgramData() {
 
     android::AutoMutex _lock(m_lock);
@@ -569,8 +697,6 @@
     ShaderProgramData* data = new ShaderProgramData;
     uint32_t currId = m_shaderProgramId;
 
-    ALOGD("%s: new data %p id %u", __FUNCTION__, data, currId);
-
     m_shaderPrograms[currId] = data;
     m_shaderProgramId++;
     return currId;
@@ -590,8 +716,6 @@
 
     ShaderProgramData* res = findObjectOrDefault(m_shaderPrograms, id);
 
-    ALOGD("%s: id=%u res=%p", __FUNCTION__, id, res);
-
     return res;
 }
 
@@ -630,9 +754,9 @@
     m_shaderProgramIdMap.erase(shaderProgramName);
 }
 
-void GLSharedGroup::initShaderProgramData(GLuint shaderProgram, GLuint numIndices) {
+void GLSharedGroup::initShaderProgramData(GLuint shaderProgram, GLuint numIndices, GLuint numAttributes) {
     ShaderProgramData* spData = getShaderProgramData(shaderProgram);
-    spData->programData.initProgramData(numIndices);
+    spData->programData.initProgramData(numIndices, numAttributes);
 }
 
 void GLSharedGroup::setShaderProgramIndexInfo(
@@ -662,3 +786,87 @@
         }
     }
 }
+
+UniformValidationInfo GLSharedGroup::getUniformValidationInfo(GLuint program) {
+    UniformValidationInfo res;
+
+    android::AutoMutex _lock(m_lock);
+
+    ProgramData* pData =
+        getProgramDataLocked(program);
+
+    if (!pData) return res;
+
+    bool error; (void)error;
+    return pData->compileValidationInfo(&error);
+}
+
+void GLSharedGroup::setProgramLinkStatus(GLuint program, GLint linkStatus) {
+    android::AutoMutex _lock(m_lock);
+    ProgramData* pData =
+        getProgramDataLocked(program);
+    if (!pData) return;
+    pData->setLinkStatus(linkStatus);
+}
+
+GLint GLSharedGroup::getProgramLinkStatus(GLuint program) {
+    android::AutoMutex _lock(m_lock);
+    ProgramData* pData = getProgramDataLocked(program);
+    if (!pData) return 0;
+    return pData->getLinkStatus();
+}
+
+void GLSharedGroup::setActiveUniformBlockCountForProgram(GLuint program, GLint count) {
+    android::AutoMutex _lock(m_lock);
+    ProgramData* pData =
+        getProgramDataLocked(program);
+
+    if (!pData) return;
+
+    pData->setActiveUniformBlockCount(count);
+}
+
+GLint GLSharedGroup::getActiveUniformBlockCount(GLuint program) {
+    android::AutoMutex _lock(m_lock);
+    ProgramData* pData =
+        getProgramDataLocked(program);
+
+    if (!pData) return 0;
+
+    return pData->getActiveUniformBlockCount();
+}
+
+void GLSharedGroup::setTransformFeedbackVaryingsCountForProgram(GLuint program, GLint count) {
+    android::AutoMutex _lock(m_lock);
+    ProgramData* pData = getProgramDataLocked(program);
+    if (!pData) return;
+    pData->setTransformFeedbackVaryingsCount(count);
+}
+
+GLint GLSharedGroup::getTransformFeedbackVaryingsCountForProgram(GLuint program) {
+    android::AutoMutex _lock(m_lock);
+    ProgramData* pData = getProgramDataLocked(program);
+    if (!pData) return 0;
+    return pData->getTransformFeedbackVaryingsCount();
+}
+
+int GLSharedGroup::getActiveUniformsCountForProgram(GLuint program) {
+    android::AutoMutex _lock(m_lock);
+    ProgramData* pData =
+        getProgramDataLocked(program);
+
+    if (!pData) return 0;
+
+    return pData->getActiveUniformsCount();
+}
+
+int GLSharedGroup::getActiveAttributesCountForProgram(GLuint program) {
+    android::AutoMutex _lock(m_lock);
+    ProgramData* pData =
+        getProgramDataLocked(program);
+
+    if (!pData) return 0;
+
+    return pData->getActiveAttributesCount();
+}
+
diff --git a/shared/OpenglCodecCommon/GLSharedGroup.h b/shared/OpenglCodecCommon/GLSharedGroup.h
index bbee27a..b423951 100755
--- a/shared/OpenglCodecCommon/GLSharedGroup.h
+++ b/shared/OpenglCodecCommon/GLSharedGroup.h
@@ -40,6 +40,7 @@
 #include "auto_goldfish_dma_context.h"
 #include "IndexRangeCache.h"
 #include "SmartPtr.h"
+#include "StateTrackingSupport.h"
 
 struct BufferData {
     BufferData();
@@ -76,10 +77,18 @@
     } IndexInfo;
 
     GLuint m_numIndexes;
+    GLuint m_numAttributes;
     IndexInfo* m_Indexes;
     bool m_initialized;
 
     std::vector<GLuint> m_shaders;
+    std::vector<GLenum> m_shaderTypes;
+
+    uint32_t m_refcount;
+    GLint m_linkStatus;
+
+    uint32_t m_activeUniformBlockCount;
+    uint32_t m_transformFeedbackVaryingsCount;;
 
 public:
     enum {
@@ -87,21 +96,56 @@
     };
 
     ProgramData();
-    void initProgramData(GLuint numIndexes);
+    void initProgramData(GLuint numIndexes, GLuint numAttributes);
     bool isInitialized();
     virtual ~ProgramData();
     void setIndexInfo(GLuint index, GLint base, GLint size, GLenum type);
     void setIndexFlags(GLuint index, GLuint flags);
     GLuint getIndexForLocation(GLint location);
     GLenum getTypeForLocation(GLint location);
+    bool isValidUniformLocation(GLint location);
 
     GLint getNextSamplerUniform(GLint index, GLint* val, GLenum* target);
     bool setSamplerUniform(GLint appLoc, GLint val, GLenum* target);
 
-    bool attachShader(GLuint shader);
+    bool attachShader(GLuint shader, GLenum shaderType);
     bool detachShader(GLuint shader);
     size_t getNumShaders() const { return m_shaders.size(); }
     GLuint getShader(size_t i) const { return m_shaders[i]; }
+
+    void incRef() { ++m_refcount; }
+    bool decRef() {
+        --m_refcount;
+        return 0 == m_refcount;
+    }
+
+    UniformValidationInfo compileValidationInfo(bool* error) const;
+    void setLinkStatus(GLint status) { m_linkStatus = status; }
+    GLint getLinkStatus() { return m_linkStatus; }
+
+    void setActiveUniformBlockCount(uint32_t count) {
+        m_activeUniformBlockCount = count;
+    }
+
+    uint32_t getActiveUniformBlockCount() const {
+        return m_activeUniformBlockCount;
+    }
+
+    void setTransformFeedbackVaryingsCount(uint32_t count) {
+        m_transformFeedbackVaryingsCount = count;
+    }
+
+    uint32_t getTransformFeedbackVaryingsCount() const {
+        return m_transformFeedbackVaryingsCount;
+    }
+
+    GLuint getActiveUniformsCount() const {
+        return m_numIndexes;
+    }
+
+    GLuint getActiveAttributesCount() const {
+        return m_numAttributes;
+    }
 };
 
 struct ShaderData {
@@ -109,6 +153,7 @@
     StringList samplerExternalNames;
     int refcount;
     std::vector<std::string> sources;
+    GLenum shaderType;
 };
 
 class ShaderProgramData {
@@ -125,6 +170,8 @@
     std::map<GLuint, ShaderData*> m_shaders;
     std::map<uint32_t, ShaderProgramData*> m_shaderPrograms;
     std::map<GLuint, uint32_t> m_shaderProgramIdMap;
+    RenderbufferInfo m_renderbufferInfo;
+    SamplerInfo m_samplerInfo;
 
     mutable android::Mutex m_lock;
 
@@ -133,12 +180,15 @@
 
     uint32_t m_shaderProgramId;
 
+    ProgramData* getProgramDataLocked(GLuint program);
 public:
     GLSharedGroup();
     ~GLSharedGroup();
     bool isShaderOrProgramObject(GLuint obj);
     BufferData * getBufferData(GLuint bufferId);
     SharedTextureDataMap* getTextureData();
+    RenderbufferInfo* getRenderbufferInfo();
+    SamplerInfo* getSamplerInfo();
     void    addBufferData(GLuint bufferId, GLsizeiptr size, const void* data);
     void    updateBufferData(GLuint bufferId, GLsizeiptr size, const void* data);
     void    setBufferUsage(GLuint bufferId, GLenum usage);
@@ -151,17 +201,22 @@
     bool    isProgram(GLuint program);
     bool    isProgramInitialized(GLuint program);
     void    addProgramData(GLuint program); 
-    void    initProgramData(GLuint program, GLuint numIndexes);
-    void    attachShader(GLuint program, GLuint shader);
-    void    detachShader(GLuint program, GLuint shader);
+    void    initProgramData(GLuint program, GLuint numIndexes, GLuint numAttributes);
+    void    refProgramData(GLuint program);
+    void    onUseProgram(GLuint previous, GLuint next);
+    bool    attachShader(GLuint program, GLuint shader);
+    bool    detachShader(GLuint program, GLuint shader);
+    bool    detachShaderLocked(GLuint program, GLuint shader);
     void    deleteProgramData(GLuint program);
+    void    deleteProgramDataLocked(GLuint program);
     void    setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type, const char* name);
     GLenum  getProgramUniformType(GLuint program, GLint location);
     GLint   getNextSamplerUniform(GLuint program, GLint index, GLint* val, GLenum* target) const;
     bool    setSamplerUniform(GLuint program, GLint appLoc, GLint val, GLenum* target);
+    bool    isProgramUniformLocationValid(GLuint program, GLint location);
 
     bool    isShader(GLuint shader);
-    bool    addShaderData(GLuint shader);
+    bool    addShaderData(GLuint shader, GLenum shaderType);
     // caller must hold a reference to the shader as long as it holds the pointer
     ShaderData* getShaderData(GLuint shader);
     void    unrefShaderData(GLuint shader);
@@ -173,8 +228,23 @@
     ShaderProgramData* getShaderProgramData(GLuint shaderProgramName);
     void deleteShaderProgramDataById(uint32_t id);
     void deleteShaderProgramData(GLuint shaderProgramName);
-    void initShaderProgramData(GLuint shaderProgram, GLuint numIndices);
+    void initShaderProgramData(GLuint shaderProgram, GLuint numIndices, GLuint numAttributes);
     void setShaderProgramIndexInfo(GLuint shaderProgram, GLuint index, GLint base, GLint size, GLenum type, const char* name);
+
+    // Validation info
+    UniformValidationInfo getUniformValidationInfo(GLuint program);
+
+    void setProgramLinkStatus(GLuint program, GLint linkStatus);
+    GLint getProgramLinkStatus(GLuint program);
+
+    void setActiveUniformBlockCountForProgram(GLuint program, GLint numBlocks);
+    GLint getActiveUniformBlockCount(GLuint program);
+
+    void setTransformFeedbackVaryingsCountForProgram(GLuint program, GLint count);
+    GLint getTransformFeedbackVaryingsCountForProgram(GLuint program);
+
+    int getActiveUniformsCountForProgram(GLuint program);
+    int getActiveAttributesCountForProgram(GLuint program);
 };
 
 typedef SmartPtr<GLSharedGroup> GLSharedGroupPtr; 
diff --git a/shared/OpenglCodecCommon/StateTrackingSupport.h b/shared/OpenglCodecCommon/StateTrackingSupport.h
index c9abf1c..c6715c4 100644
--- a/shared/OpenglCodecCommon/StateTrackingSupport.h
+++ b/shared/OpenglCodecCommon/StateTrackingSupport.h
@@ -17,21 +17,24 @@
 #define _STATE_TRACKING_SUPPORT_H_
 
 #include "android/base/containers/HybridComponentManager.h"
+#include "android/base/synchronization/AndroidLock.h"
 
-template <bool initialIsTrue>
+#include <GLES2/gl2.h>
+
+template <class IndexType, bool initialIsTrue>
 class PredicateMap {
 public:
     static const uint64_t kBitsPerEntry = 64;
-    void add(uint32_t objId) {
+    void add(IndexType objId) {
         static const uint64_t kNone = 0ULL;
         static const uint64_t kAll = ~kNone;
-        uint32_t index = objId / kBitsPerEntry;
+        IndexType index = objId / kBitsPerEntry;
         if (!mStorage.get_const(index)) {
             mStorage.add(index, initialIsTrue ? kAll : kNone);
         }
     }
 
-    void remove(uint32_t objId) {
+    void remove(IndexType objId) {
         if (initialIsTrue) {
             set(objId, true);
         } else {
@@ -39,8 +42,8 @@
         }
     }
 
-    void set(uint32_t objId, bool predicate) {
-        uint32_t index = objId / kBitsPerEntry;
+    void set(IndexType objId, bool predicate) {
+        IndexType index = objId / kBitsPerEntry;
 
         if (!mStorage.get_const(index)) return;
 
@@ -55,8 +58,8 @@
         }
     }
 
-    bool get(uint32_t objId) const {
-        uint32_t index = objId / kBitsPerEntry;
+    bool get(IndexType objId) const {
+        IndexType index = objId / kBitsPerEntry;
 
         const uint64_t* current = mStorage.get_const(index);
 
@@ -67,8 +70,170 @@
     }
 
 private:
-    using Storage = android::base::HybridComponentManager<10000, uint32_t, uint64_t>;
+    using Storage = android::base::HybridComponentManager<10000, IndexType, uint64_t>;
     Storage mStorage;
 };
 
+// A structure for fast validation of uniform uploads and other uniform related api calls.
+struct UniformLocationInfo {
+    bool valid = false;
+    uint32_t columns;
+    uint32_t rows;
+    bool isSampler;
+    bool isInt;
+    bool isArray;
+    bool isUnsigned;
+    bool isBool;
+};
+
+using UniformValidationInfo = android::base::HybridComponentManager<1000, uint32_t, UniformLocationInfo>;
+
+using LastQueryTargetInfo = android::base::HybridComponentManager<1000, uint32_t, uint32_t>;
+
+using ExistenceMap = PredicateMap<uint32_t, false>;
+
+struct RboProps {
+    GLenum format;
+    GLsizei multisamples;
+    GLsizei width;
+    GLsizei height;
+    bool previouslyBound;
+    uint32_t refcount;
+    bool boundEGLImage;
+};
+
+struct SamplerProps {
+    uint32_t refcount;
+};
+
+template <class T>
+class ScopedLockedView {
+public:
+    ScopedLockedView(T* info) : mInfo(info) {
+        mInfo->lock();
+    }
+    virtual ~ScopedLockedView() {
+        mInfo->unlock();
+    }
+protected:
+    T* mInfo;
+
+    T* internalInfo() { return mInfo; }
+    const T* internalInfo_const() const { return mInfo; }
+};
+
+struct RenderbufferInfo {
+    android::base::guest::Lock infoLock;
+    android::base::HybridComponentManager<1000, uint32_t, RboProps> component;
+
+    void lock() { infoLock.lock(); }
+    void unlock() { infoLock.unlock(); }
+
+    class ScopedView : public ScopedLockedView<RenderbufferInfo> {
+        public:
+            ScopedView(RenderbufferInfo* info) : ScopedLockedView<RenderbufferInfo>(info) { }
+            bool hasRbo(GLuint id) const {
+                const RboProps* info = internalInfo_const()->component.get_const(id);
+                if (!info) return false;
+                return 0 != info->refcount;
+            }
+            virtual ~ScopedView() = default;
+            RboProps* get(GLuint id) {
+                return internalInfo()->component.get(id);
+            }
+            const RboProps* get_const(GLuint id) {
+                return internalInfo_const()->component.get_const(id);
+            }
+            void addFresh(GLuint id) {
+                RboProps props;
+                props.format = GL_NONE;
+                props.multisamples = 0;
+                props.width = 0;
+                props.height = 0;
+                props.previouslyBound = false;
+                props.refcount = 1;
+                props.boundEGLImage = false;
+                internalInfo()->component.add(id, props);
+            }
+            RboProps* bind(GLuint id) {
+                if (!hasRbo(id)) addFresh(id);
+                ref(id);
+                RboProps* res = get(id);
+                res->previouslyBound = true;
+                return res;
+            }
+            void ref(GLuint id) {
+                RboProps* props = get(id);
+                if (!props) return;
+                ++props->refcount;
+            }
+            bool unref(GLuint id) {
+                RboProps* props = get(id);
+                if (!props) return false;
+                if (!props->refcount) return false;
+                --props->refcount;
+                bool gone = 0 == props->refcount;
+                if (gone) {
+                    props->format = 0;
+                    props->multisamples = 0;
+                    props->width = 0;
+                    props->height = 0;
+                    props->previouslyBound = false;
+                    props->boundEGLImage = false;
+                }
+                return gone;
+            }
+    };
+};
+
+struct SamplerInfo {
+    android::base::guest::Lock infoLock;
+    android::base::HybridComponentManager<1000, uint32_t, SamplerProps> component;
+
+    void lock() { infoLock.lock(); }
+    void unlock() { infoLock.unlock(); }
+
+    class ScopedView : public ScopedLockedView<SamplerInfo> {
+        public:
+            ScopedView(SamplerInfo* info) : ScopedLockedView<SamplerInfo>(info) { }
+            bool samplerExists(GLuint id) const {
+                const SamplerProps* info = internalInfo_const()->component.get_const(id);
+                if (!info) return false;
+                return 0 != info->refcount;
+            }
+            virtual ~ScopedView() = default;
+            SamplerProps* get(GLuint id) {
+                return internalInfo()->component.get(id);
+            }
+            const SamplerProps* get_const(GLuint id) {
+                return internalInfo_const()->component.get_const(id);
+            }
+            void addFresh(GLuint id) {
+                SamplerProps props;
+                props.refcount = 1;
+                internalInfo()->component.add(id, props);
+            }
+            SamplerProps* bind(GLuint id) {
+                if (!samplerExists(id)) return 0;
+                ref(id);
+                SamplerProps* res = get(id);
+                return res;
+            }
+            void ref(GLuint id) {
+                SamplerProps* props = get(id);
+                if (!props) return;
+                ++props->refcount;
+            }
+            bool unref(GLuint id) {
+                SamplerProps* props = get(id);
+                if (!props) return false;
+                if (!props->refcount) return false;
+                --props->refcount;
+                bool gone = 0 == props->refcount;
+                return gone;
+            }
+    };
+};
+
+
 #endif
diff --git a/shared/OpenglCodecCommon/TextureSharedData.h b/shared/OpenglCodecCommon/TextureSharedData.h
index 1372f7a..96827ef 100644
--- a/shared/OpenglCodecCommon/TextureSharedData.h
+++ b/shared/OpenglCodecCommon/TextureSharedData.h
@@ -35,6 +35,13 @@
     TextureDims* dims;
     bool immutable;
     bool boundEGLImage;
+    bool hasStorage;
+    bool hasCubeNegX;
+    bool hasCubePosX;
+    bool hasCubeNegY;
+    bool hasCubePosY;
+    bool hasCubeNegZ;
+    bool hasCubePosZ;
 };
 
 typedef std::map<GLuint, TextureRec*> SharedTextureDataMap;
diff --git a/shared/OpenglCodecCommon/astc-codec.h b/shared/OpenglCodecCommon/astc-codec.h
new file mode 100644
index 0000000..e15fe47
--- /dev/null
+++ b/shared/OpenglCodecCommon/astc-codec.h
@@ -0,0 +1,47 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ASTC_CODEC_ASTC_CODEC_H_
+#define ASTC_CODEC_ASTC_CODEC_H_
+
+namespace astc_codec {
+
+// These are the valid ASTC footprints according to the specification in
+// Section C.2.7.
+enum FootprintType {
+  k4x4,
+  k5x4,
+  k5x5,
+  k6x5,
+  k6x6,
+  k8x5,
+  k8x6,
+  k10x5,
+  k10x6,
+  k8x8,
+  k10x8,
+  k10x10,
+  k12x10,
+  k12x12,
+
+  kCount
+};
+
+struct AstcFootprint {
+    AstcFootprint(int width, int height);
+};
+
+}  // namespace astc_codec
+
+#endif  // ASTC_CODEC_ASTC_CODEC_H_
diff --git a/shared/OpenglCodecCommon/etc.cpp b/shared/OpenglCodecCommon/etc.cpp
new file mode 100644
index 0000000..dea3228
--- /dev/null
+++ b/shared/OpenglCodecCommon/etc.cpp
@@ -0,0 +1,1031 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "etc.h"
+
+#include <algorithm>
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+typedef uint16_t etc1_uint16;
+
+/* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
+
+ The number of bits that represent a 4x4 texel block is 64 bits if
+ <internalformat> is given by ETC1_RGB8_OES.
+
+ The data for a block is a number of bytes,
+
+ {q0, q1, q2, q3, q4, q5, q6, q7}
+
+ where byte q0 is located at the lowest memory address and q7 at
+ the highest. The 64 bits specifying the block is then represented
+ by the following 64 bit integer:
+
+ int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
+
+ ETC1_RGB8_OES:
+
+ a) bit layout in bits 63 through 32 if diffbit = 0
+
+ 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
+ -----------------------------------------------
+ | base col1 | base col2 | base col1 | base col2 |
+ | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
+ -----------------------------------------------
+
+ 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
+ ---------------------------------------------------
+ | base col1 | base col2 | table  | table  |diff|flip|
+ | B1 (4bits)| B2 (4bits)| cw 1   | cw 2   |bit |bit |
+ ---------------------------------------------------
+
+
+ b) bit layout in bits 63 through 32 if diffbit = 1
+
+ 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
+ -----------------------------------------------
+ | base col1    | dcol 2 | base col1    | dcol 2 |
+ | R1' (5 bits) | dR2    | G1' (5 bits) | dG2    |
+ -----------------------------------------------
+
+ 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
+ ---------------------------------------------------
+ | base col 1   | dcol 2 | table  | table  |diff|flip|
+ | B1' (5 bits) | dB2    | cw 1   | cw 2   |bit |bit |
+ ---------------------------------------------------
+
+
+ c) bit layout in bits 31 through 0 (in both cases)
+
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+ -----------------------------------------------
+ |       most significant pixel index bits       |
+ | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a|
+ -----------------------------------------------
+
+ 15 14 13 12 11 10  9  8  7  6  5  4  3   2   1  0
+ --------------------------------------------------
+ |         least significant pixel index bits       |
+ | p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
+ --------------------------------------------------
+
+
+ Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
+
+ table codeword                modifier table
+ ------------------        ----------------------
+ 0                     -8  -2  2   8
+ 1                    -17  -5  5  17
+ 2                    -29  -9  9  29
+ 3                    -42 -13 13  42
+ 4                    -60 -18 18  60
+ 5                    -80 -24 24  80
+ 6                   -106 -33 33 106
+ 7                   -183 -47 47 183
+
+
+ Add table 3.17.3 Mapping from pixel index values to modifier values for
+ ETC1 compressed textures:
+
+ pixel index value
+ ---------------
+ msb     lsb           resulting modifier value
+ -----   -----          -------------------------
+ 1       1            -b (large negative value)
+ 1       0            -a (small negative value)
+ 0       0             a (small positive value)
+ 0       1             b (large positive value)
+
+ ETC2 codec:
+     from https://www.khronos.org/registry/gles/specs/3.0/es_spec_3.0.4.pdf
+     page 289
+ */
+
+static const int kRGBModifierTable[] = {
+/* 0 */2, 8, -2, -8,
+/* 1 */5, 17, -5, -17,
+/* 2 */9, 29, -9, -29,
+/* 3 */13, 42, -13, -42,
+/* 4 */18, 60, -18, -60,
+/* 5 */24, 80, -24, -80,
+/* 6 */33, 106, -33, -106,
+/* 7 */47, 183, -47, -183 };
+
+static const int kRGBOpaqueModifierTable[] = {
+/* 0 */0, 8, 0, -8,
+/* 1 */0, 17, 0, -17,
+/* 2 */0, 29, 0, -29,
+/* 3 */0, 42, 0, -42,
+/* 4 */0, 60, 0, -60,
+/* 5 */0, 80, 0, -80,
+/* 6 */0, 106, 0, -106,
+/* 7 */0, 183, 0, -183 };
+
+static const int kAlphaModifierTable[] = {
+/* 0 */ -3, -6, -9, -15, 2, 5, 8, 14,
+/* 1 */ -3, -7, -10, -13, 2, 6, 9, 12,
+/* 2 */ -2, -5, -8, -13, 1, 4, 7, 12,
+/* 3 */ -2, -4, -6, -13, 1, 3, 5, 12,
+/* 4 */ -3, -6, -8, -12, 2, 5, 7, 11,
+/* 5 */ -3, -7, -9, -11, 2, 6, 8, 10,
+/* 6 */ -4, -7, -8, -11, 3, 6, 7, 10,
+/* 7 */ -3, -5, -8, -11, 2, 4, 7, 10,
+/* 8 */ -2, -6, -8, -10, 1, 5, 7, 9,
+/* 9 */ -2, -5, -8, -10, 1, 4, 7, 9,
+/* 10 */ -2, -4, -8, -10, 1, 3, 7, 9,
+/* 11 */ -2, -5, -7, -10, 1, 4, 6, 9,
+/* 12 */ -3, -4, -7, -10, 2, 3, 6, 9,
+/* 13 */ -1, -2, -3, -10, 0, 1, 2, 9,
+/* 14 */ -4, -6, -8, -9, 3, 5, 7, 8,
+/* 15 */ -3, -5, -7, -9, 2, 4, 6, 8
+};
+
+static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
+
+static inline int clamp(int x) {
+    return (x >= 0 ? (x < 255 ? x : 255) : 0);
+}
+
+static inline int clamp2047(int x) {
+    return (x >= 0 ? (x < 2047 ? x : 2047) : 0);
+}
+
+static inline int clampSigned1023(int x) {
+    return (x >= -1023 ? (x < 1023 ? x : 1023) : -1023);
+}
+
+static
+inline int convert4To8(int b) {
+    int c = b & 0xf;
+    return (c << 4) | c;
+}
+
+static
+inline int convert5To8(int b) {
+    int c = b & 0x1f;
+    return (c << 3) | (c >> 2);
+}
+
+static
+inline int convert6To8(int b) {
+    int c = b & 0x3f;
+    return (c << 2) | (c >> 4);
+}
+
+static
+inline int convert7To8(int b) {
+    int c = b & 0x7f;
+    return (c << 1) | (c >> 6);
+}
+
+static
+inline int divideBy255(int d) {
+    return (d + 128 + (d >> 8)) >> 8;
+}
+
+static
+inline int convert8To4(int b) {
+    int c = b & 0xff;
+    return divideBy255(c * 15);
+}
+
+static
+inline int convert8To5(int b) {
+    int c = b & 0xff;
+    return divideBy255(c * 31);
+}
+
+static
+inline int convertDiff(int base, int diff) {
+    return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
+}
+static
+int isOverflowed(int base, int diff) {
+    int val = (0x1f & base) + kLookup[0x7 & diff];
+    return val < 0 || val >= 32;
+}
+
+static
+void decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
+        etc1_uint32 low, bool second, bool flipped, bool isPunchthroughAlpha,
+        bool opaque) {
+    int baseX = 0;
+    int baseY = 0;
+    int channels = isPunchthroughAlpha ? 4 : 3;
+    if (second) {
+        if (flipped) {
+            baseY = 2;
+        } else {
+            baseX = 2;
+        }
+    }
+    for (int i = 0; i < 8; i++) {
+        int x, y;
+        if (flipped) {
+            x = baseX + (i >> 1);
+            y = baseY + (i & 1);
+        } else {
+            x = baseX + (i >> 2);
+            y = baseY + (i & 3);
+        }
+        int k = y + (x * 4);
+        int msb = ((low >> (k + 15)) & 2);
+        int lsb = ((low >> k) & 1);
+        etc1_byte* q = pOut + channels * (x + 4 * y);
+        if (isPunchthroughAlpha && !opaque && msb && !lsb) {
+            // rgba all 0
+            memset(q, 0, 4);
+            q += 4;
+        } else {
+            int offset = lsb | msb;
+            int delta = table[offset];
+            *q++ = clamp(r + delta);
+            *q++ = clamp(g + delta);
+            *q++ = clamp(b + delta);
+            if (isPunchthroughAlpha) {
+                *q++ = 255;
+            }
+        }
+    }
+}
+
+static void etc2_T_H_index(const int* clrTable, etc1_uint32 low,
+                           bool isPunchthroughAlpha, bool opaque,
+                           etc1_byte* pOut) {
+    etc1_byte* q = pOut;
+    for (int y = 0; y < 4; y++) {
+        for (int x = 0; x < 4; x++) {
+            int k = y + x * 4;
+            int msb = (low >> (k + 15)) & 2;
+            int lsb = (low >> k) & 1;
+            if (isPunchthroughAlpha && !opaque && msb && !lsb) {
+                // rgba all 0
+                memset(q, 0, 4);
+                q += 4;
+            } else {
+                int offset = lsb | msb;
+                for (int c = 0; c < 3; c++) {
+                    *q++ = clrTable[offset*3 + c];
+                }
+                if (isPunchthroughAlpha) {
+                    *q++ = 255;
+                }
+            }
+        }
+    }
+}
+
+// ETC2 codec:
+//     from https://www.khronos.org/registry/gles/specs/3.0/es_spec_3.0.4.pdf
+//     page 289
+
+static void etc2_decode_block_T(etc1_uint32 high, etc1_uint32 low,
+        bool isPunchthroughAlpha, bool opaque, etc1_byte* pOut) {
+    const int LUT[] = {3, 6, 11, 16, 23, 32, 41, 64};
+    int r1, r2, g1, g2, b1, b2;
+    r1 = convert4To8((((high >> 27) & 3) << 2) | ((high >> 24) & 3));
+    g1 = convert4To8(high >> 20);
+    b1 = convert4To8(high >> 16);
+    r2 = convert4To8(high >> 12);
+    g2 = convert4To8(high >> 8);
+    b2 = convert4To8(high >> 4);
+    // 3 bits intense modifier
+    int intenseIdx = (((high >> 2) & 3) << 1) | (high & 1);
+    int intenseMod = LUT[intenseIdx];
+    int clrTable[12];
+    clrTable[0] = r1;
+    clrTable[1] = g1;
+    clrTable[2] = b1;
+    clrTable[3] = clamp(r2 + intenseMod);
+    clrTable[4] = clamp(g2 + intenseMod);
+    clrTable[5] = clamp(b2 + intenseMod);
+    clrTable[6] = r2;
+    clrTable[7] = g2;
+    clrTable[8] = b2;
+    clrTable[9] = clamp(r2 - intenseMod);
+    clrTable[10] = clamp(g2 - intenseMod);
+    clrTable[11] = clamp(b2 - intenseMod);
+    etc2_T_H_index(clrTable, low, isPunchthroughAlpha, opaque, pOut);
+}
+
+static void etc2_decode_block_H(etc1_uint32 high, etc1_uint32 low,
+        bool isPunchthroughAlpha, bool opaque, etc1_byte* pOut) {
+    const int LUT[] = {3, 6, 11, 16, 23, 32, 41, 64};
+    int r1, r2, g1, g2, b1, b2;
+    r1 = convert4To8(high >> 27);
+    g1 = convert4To8((high >> 24) << 1 | ((high >> 20) & 1));
+    b1 = convert4To8((high >> 19) << 3 | ((high >> 15) & 7));
+    r2 = convert4To8(high >> 11);
+    g2 = convert4To8(high >> 7);
+    b2 = convert4To8(high >> 3);
+    // 3 bits intense modifier
+    int intenseIdx = high & 4;
+    intenseIdx |= (high & 1) << 1;
+    intenseIdx |= (((r1 << 16) | (g1 << 8) | b1) >= ((r2 << 16) | (g2 << 8) | b2));
+    int intenseMod = LUT[intenseIdx];
+    int clrTable[12];
+    clrTable[0] = clamp(r1 + intenseMod);
+    clrTable[1] = clamp(g1 + intenseMod);
+    clrTable[2] = clamp(b1 + intenseMod);
+    clrTable[3] = clamp(r1 - intenseMod);
+    clrTable[4] = clamp(g1 - intenseMod);
+    clrTable[5] = clamp(b1 - intenseMod);
+    clrTable[6] = clamp(r2 + intenseMod);
+    clrTable[7] = clamp(g2 + intenseMod);
+    clrTable[8] = clamp(b2 + intenseMod);
+    clrTable[9] = clamp(r2 - intenseMod);
+    clrTable[10] = clamp(g2 - intenseMod);
+    clrTable[11] = clamp(b2 - intenseMod);
+    etc2_T_H_index(clrTable, low, isPunchthroughAlpha, opaque, pOut);
+}
+
+static void etc2_decode_block_P(etc1_uint32 high, etc1_uint32 low,
+        bool isPunchthroughAlpha, etc1_byte* pOut) {
+    int ro, go, bo, rh, gh, bh, rv, gv, bv;
+    uint64_t data = high;
+    data = data << 32 | low;
+    ro = convert6To8(data >> 57);
+    go = convert7To8((data >> 56 << 6) | ((data >> 49) & 63));
+    bo = convert6To8((data >> 48 << 5)
+            | (((data >> 43) & 3 ) << 3)
+            | ((data >> 39) & 7));
+    rh = convert6To8((data >> 34 << 1) | ((data >> 32) & 1));
+    gh = convert7To8(data >> 25);
+    bh = convert6To8(data >> 19);
+    rv = convert6To8(data >> 13);
+    gv = convert7To8(data >> 6);
+    bv = convert6To8(data);
+    etc1_byte* q = pOut;
+    for (int i = 0; i < 16; i++) {
+        int y = i >> 2;
+        int x = i & 3;
+        *q++ = clamp((x * (rh - ro) + y * (rv - ro) + 4 * ro + 2) >> 2);
+        *q++ = clamp((x * (gh - go) + y * (gv - go) + 4 * go + 2) >> 2);
+        *q++ = clamp((x * (bh - bo) + y * (bv - bo) + 4 * bo + 2) >> 2);
+        if (isPunchthroughAlpha) *q++ = 255;
+    }
+}
+
+// Input is an ETC1 / ETC2 compressed version of the data.
+// Output is a 4 x 4 square of 3-byte pixels in form R, G, B
+// ETC2 codec:
+//     from https://www.khronos.org/registry/gles/specs/3.0/es_spec_3.0.4.pdf
+//     page 289
+
+void etc2_decode_rgb_block(const etc1_byte* pIn, bool isPunchthroughAlpha,
+                           etc1_byte* pOut) {
+    etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
+    etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
+    bool opaque = (high >> 1) & 1;
+    int r1, r2, g1, g2, b1, b2;
+    if (isPunchthroughAlpha || high & 2) {
+        // differential
+        int rBase = high >> 27;
+        int gBase = high >> 19;
+        int bBase = high >> 11;
+        if (isOverflowed(rBase, high >> 24)) {
+            etc2_decode_block_T(high, low, isPunchthroughAlpha, opaque, pOut);
+            return;
+        }
+        if (isOverflowed(gBase, high >> 16)) {
+            etc2_decode_block_H(high, low, isPunchthroughAlpha, opaque, pOut);
+            return;
+        }
+        if (isOverflowed(bBase, high >> 8)) {
+            etc2_decode_block_P(high, low, isPunchthroughAlpha, pOut);
+            return;
+        }
+        r1 = convert5To8(rBase);
+        r2 = convertDiff(rBase, high >> 24);
+        g1 = convert5To8(gBase);
+        g2 = convertDiff(gBase, high >> 16);
+        b1 = convert5To8(bBase);
+        b2 = convertDiff(bBase, high >> 8);
+    } else {
+        // not differential
+        r1 = convert4To8(high >> 28);
+        r2 = convert4To8(high >> 24);
+        g1 = convert4To8(high >> 20);
+        g2 = convert4To8(high >> 16);
+        b1 = convert4To8(high >> 12);
+        b2 = convert4To8(high >> 8);
+    }
+    int tableIndexA = 7 & (high >> 5);
+    int tableIndexB = 7 & (high >> 2);
+    const int* rgbModifierTable = opaque || !isPunchthroughAlpha ?
+                                  kRGBModifierTable : kRGBOpaqueModifierTable;
+    const int* tableA = rgbModifierTable + tableIndexA * 4;
+    const int* tableB = rgbModifierTable + tableIndexB * 4;
+    bool flipped = (high & 1) != 0;
+    decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped,
+                    isPunchthroughAlpha, opaque);
+    decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped,
+                    isPunchthroughAlpha, opaque);
+}
+
+void eac_decode_single_channel_block(const etc1_byte* pIn,
+                                     int decodedElementBytes, bool isSigned,
+                                     etc1_byte* pOut) {
+    assert(decodedElementBytes == 1 || decodedElementBytes == 2 || decodedElementBytes == 4);
+    int base_codeword = isSigned ? reinterpret_cast<const char*>(pIn)[0]
+                                 : pIn[0];
+    if (base_codeword == -128) base_codeword = -127;
+    int multiplier = pIn[1] >> 4;
+    int tblIdx = pIn[1] & 15;
+    const int* table = kAlphaModifierTable + tblIdx * 8;
+    const etc1_byte* p = pIn + 2;
+    // position in a byte of the next 3-bit index:
+    // | a a a | b b b | c c c | d d d ...
+    // | byte               | byte...
+    int bitOffset = 5;
+    for (int i = 0; i < 16; i ++) {
+        // flip x, y in output
+        int outIdx = (i % 4) * 4 + i / 4;
+        etc1_byte* q = pOut + outIdx * decodedElementBytes;
+
+        int modifier = 0;
+        if (bitOffset < 0) { // (Part of) the index is in the next byte.
+            modifier += p[0] << (-bitOffset);
+            p ++;
+            bitOffset += 8;
+        }
+        modifier += p[0] >> bitOffset;
+        modifier &= 7;
+        bitOffset -= 3; // move to the next index
+        if (bitOffset == -3) {
+            bitOffset = 5;
+            p++;
+        }
+        int modifierValue = table[modifier];
+        int decoded = base_codeword + modifierValue * multiplier;
+        if (decodedElementBytes == 1) {
+            *q = clamp(decoded);
+        } else { // decodedElementBytes == 4
+            decoded *= 8;
+            if (multiplier == 0) {
+                decoded += modifierValue;
+            }
+            if (isSigned) {
+                decoded = clampSigned1023(decoded);
+                reinterpret_cast<float*>(q)[0] = (float)decoded / 1023.0;
+            } else {
+                decoded += 4;
+                decoded = clamp2047(decoded);
+                reinterpret_cast<float*>(q)[0] = (float)decoded / 2047.0;
+            }
+        }
+    }
+}
+
+typedef struct {
+    etc1_uint32 high;
+    etc1_uint32 low;
+    etc1_uint32 score; // Lower is more accurate
+} etc_compressed;
+
+static
+inline void take_best(etc_compressed* a, const etc_compressed* b) {
+    if (a->score > b->score) {
+        *a = *b;
+    }
+}
+
+static
+void etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
+        etc1_byte* pColors, bool flipped, bool second) {
+    int r = 0;
+    int g = 0;
+    int b = 0;
+
+    if (flipped) {
+        int by = 0;
+        if (second) {
+            by = 2;
+        }
+        for (int y = 0; y < 2; y++) {
+            int yy = by + y;
+            for (int x = 0; x < 4; x++) {
+                int i = x + 4 * yy;
+                if (inMask & (1 << i)) {
+                    const etc1_byte* p = pIn + i * 3;
+                    r += *(p++);
+                    g += *(p++);
+                    b += *(p++);
+                }
+            }
+        }
+    } else {
+        int bx = 0;
+        if (second) {
+            bx = 2;
+        }
+        for (int y = 0; y < 4; y++) {
+            for (int x = 0; x < 2; x++) {
+                int xx = bx + x;
+                int i = xx + 4 * y;
+                if (inMask & (1 << i)) {
+                    const etc1_byte* p = pIn + i * 3;
+                    r += *(p++);
+                    g += *(p++);
+                    b += *(p++);
+                }
+            }
+        }
+    }
+    pColors[0] = (etc1_byte)((r + 4) >> 3);
+    pColors[1] = (etc1_byte)((g + 4) >> 3);
+    pColors[2] = (etc1_byte)((b + 4) >> 3);
+}
+
+static
+inline int square(int x) {
+    return x * x;
+}
+
+static etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
+        const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex,
+        const int* pModifierTable) {
+    etc1_uint32 bestScore = ~0;
+    int bestIndex = 0;
+    int pixelR = pIn[0];
+    int pixelG = pIn[1];
+    int pixelB = pIn[2];
+    int r = pBaseColors[0];
+    int g = pBaseColors[1];
+    int b = pBaseColors[2];
+    for (int i = 0; i < 4; i++) {
+        int modifier = pModifierTable[i];
+        int decodedG = clamp(g + modifier);
+        etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG));
+        if (score >= bestScore) {
+            continue;
+        }
+        int decodedR = clamp(r + modifier);
+        score += (etc1_uint32) (3 * square(decodedR - pixelR));
+        if (score >= bestScore) {
+            continue;
+        }
+        int decodedB = clamp(b + modifier);
+        score += (etc1_uint32) square(decodedB - pixelB);
+        if (score < bestScore) {
+            bestScore = score;
+            bestIndex = i;
+        }
+    }
+    etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
+            << bitIndex;
+    *pLow |= lowMask;
+    return bestScore;
+}
+
+static
+void etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask,
+        etc_compressed* pCompressed, bool flipped, bool second,
+        const etc1_byte* pBaseColors, const int* pModifierTable) {
+    int score = pCompressed->score;
+    if (flipped) {
+        int by = 0;
+        if (second) {
+            by = 2;
+        }
+        for (int y = 0; y < 2; y++) {
+            int yy = by + y;
+            for (int x = 0; x < 4; x++) {
+                int i = x + 4 * yy;
+                if (inMask & (1 << i)) {
+                    score += chooseModifier(pBaseColors, pIn + i * 3,
+                            &pCompressed->low, yy + x * 4, pModifierTable);
+                }
+            }
+        }
+    } else {
+        int bx = 0;
+        if (second) {
+            bx = 2;
+        }
+        for (int y = 0; y < 4; y++) {
+            for (int x = 0; x < 2; x++) {
+                int xx = bx + x;
+                int i = xx + 4 * y;
+                if (inMask & (1 << i)) {
+                    score += chooseModifier(pBaseColors, pIn + i * 3,
+                            &pCompressed->low, y + xx * 4, pModifierTable);
+                }
+            }
+        }
+    }
+    pCompressed->score = score;
+}
+
+static bool inRange4bitSigned(int color) {
+    return color >= -4 && color <= 3;
+}
+
+static void etc_encodeBaseColors(etc1_byte* pBaseColors,
+        const etc1_byte* pColors, etc_compressed* pCompressed) {
+    int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks
+    bool differential;
+    {
+        int r51 = convert8To5(pColors[0]);
+        int g51 = convert8To5(pColors[1]);
+        int b51 = convert8To5(pColors[2]);
+        int r52 = convert8To5(pColors[3]);
+        int g52 = convert8To5(pColors[4]);
+        int b52 = convert8To5(pColors[5]);
+
+        r1 = convert5To8(r51);
+        g1 = convert5To8(g51);
+        b1 = convert5To8(b51);
+
+        int dr = r52 - r51;
+        int dg = g52 - g51;
+        int db = b52 - b51;
+
+        differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
+                && inRange4bitSigned(db);
+        if (differential) {
+            r2 = convert5To8(r51 + dr);
+            g2 = convert5To8(g51 + dg);
+            b2 = convert5To8(b51 + db);
+            pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
+                    | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
+        }
+    }
+
+    if (!differential) {
+        int r41 = convert8To4(pColors[0]);
+        int g41 = convert8To4(pColors[1]);
+        int b41 = convert8To4(pColors[2]);
+        int r42 = convert8To4(pColors[3]);
+        int g42 = convert8To4(pColors[4]);
+        int b42 = convert8To4(pColors[5]);
+        r1 = convert4To8(r41);
+        g1 = convert4To8(g41);
+        b1 = convert4To8(b41);
+        r2 = convert4To8(r42);
+        g2 = convert4To8(g42);
+        b2 = convert4To8(b42);
+        pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
+                << 16) | (b41 << 12) | (b42 << 8);
+    }
+    pBaseColors[0] = r1;
+    pBaseColors[1] = g1;
+    pBaseColors[2] = b1;
+    pBaseColors[3] = r2;
+    pBaseColors[4] = g2;
+    pBaseColors[5] = b2;
+}
+
+static
+void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
+        const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
+    pCompressed->score = ~0;
+    pCompressed->high = (flipped ? 1 : 0);
+    pCompressed->low = 0;
+
+    etc1_byte pBaseColors[6];
+
+    etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
+
+    int originalHigh = pCompressed->high;
+
+    const int* pModifierTable = kRGBModifierTable;
+    for (int i = 0; i < 8; i++, pModifierTable += 4) {
+        etc_compressed temp;
+        temp.score = 0;
+        temp.high = originalHigh | (i << 5);
+        temp.low = 0;
+        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
+                pBaseColors, pModifierTable);
+        take_best(pCompressed, &temp);
+    }
+    pModifierTable = kRGBModifierTable;
+    etc_compressed firstHalf = *pCompressed;
+    for (int i = 0; i < 8; i++, pModifierTable += 4) {
+        etc_compressed temp;
+        temp.score = firstHalf.score;
+        temp.high = firstHalf.high | (i << 2);
+        temp.low = firstHalf.low;
+        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
+                pBaseColors + 3, pModifierTable);
+        if (i == 0) {
+            *pCompressed = temp;
+        } else {
+            take_best(pCompressed, &temp);
+        }
+    }
+}
+
+static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
+    pOut[0] = (etc1_byte)(d >> 24);
+    pOut[1] = (etc1_byte)(d >> 16);
+    pOut[2] = (etc1_byte)(d >> 8);
+    pOut[3] = (etc1_byte) d;
+}
+
+// Input is a 4 x 4 square of 3-byte pixels in form R, G, B
+// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
+// pixel is valid or not. Invalid pixel color values are ignored when compressing.
+// Output is an ETC1 compressed version of the data.
+
+void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
+        etc1_byte* pOut) {
+    etc1_byte colors[6];
+    etc1_byte flippedColors[6];
+    etc_average_colors_subblock(pIn, inMask, colors, false, false);
+    etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
+    etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
+    etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
+
+    etc_compressed a, b;
+    etc_encode_block_helper(pIn, inMask, colors, &a, false);
+    etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
+    take_best(&a, &b);
+    writeBigEndian(pOut, a.high);
+    writeBigEndian(pOut + 4, a.low);
+}
+
+// Return the size of the encoded image data (does not include size of PKM header).
+
+etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
+    return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
+}
+
+etc1_uint32 etc_get_encoded_data_size(ETC2ImageFormat format, etc1_uint32 width,
+                                      etc1_uint32 height) {
+    etc1_uint32 size = ((width + 3) & ~3) * ((height + 3) & ~3);
+    switch (format) {
+        case EtcRGB8:
+        case EtcRGB8A1:
+        case EtcR11:
+        case EtcSignedR11:
+            return size >> 1;
+        case EtcRG11:
+        case EtcSignedRG11:
+        case EtcRGBA8:
+            return size;
+        default:
+            assert(0);
+            return 0;
+    }
+}
+
+etc1_uint32 etc_get_decoded_pixel_size(ETC2ImageFormat format) {
+    switch (format) {
+        case EtcRGB8:
+            return 3;
+        case EtcRGBA8:
+            return 4;
+        case EtcRGB8A1:
+        case EtcR11:
+        case EtcSignedR11:
+            return 4;
+        case EtcRG11:
+        case EtcSignedRG11:
+            return 8;
+        default:
+            assert(0);
+            return 0;
+    }
+}
+
+// Encode an entire image.
+// pIn - pointer to the image data. Formatted such that the Red component of
+//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
+// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
+
+int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
+        etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
+    if (pixelSize < 2 || pixelSize > 3) {
+        return -1;
+    }
+    static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
+    static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
+            0xffff };
+    etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
+    etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
+
+    etc1_uint32 encodedWidth = (width + 3) & ~3;
+    etc1_uint32 encodedHeight = (height + 3) & ~3;
+
+    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
+        etc1_uint32 yEnd = height - y;
+        if (yEnd > 4) {
+            yEnd = 4;
+        }
+        int ymask = kYMask[yEnd];
+        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
+            etc1_uint32 xEnd = width - x;
+            if (xEnd > 4) {
+                xEnd = 4;
+            }
+            int mask = ymask & kXMask[xEnd];
+            for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
+                etc1_byte* q = block + (cy * 4) * 3;
+                const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
+                if (pixelSize == 3) {
+                    memcpy(q, p, xEnd * 3);
+                } else {
+                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
+                        int pixel = (p[1] << 8) | p[0];
+                        *q++ = convert5To8(pixel >> 11);
+                        *q++ = convert6To8(pixel >> 5);
+                        *q++ = convert5To8(pixel);
+                        p += pixelSize;
+                    }
+                }
+            }
+            etc1_encode_block(block, mask, encoded);
+            memcpy(pOut, encoded, sizeof(encoded));
+            pOut += sizeof(encoded);
+        }
+    }
+    return 0;
+}
+
+// Decode an entire image.
+// pIn - pointer to encoded data.
+// pOut - pointer to the image data. Will be written such that the Red component of
+//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
+//        large enough to store entire image.
+
+
+int etc2_decode_image(const etc1_byte* pIn, ETC2ImageFormat format,
+        etc1_byte* pOut,
+        etc1_uint32 width, etc1_uint32 height,
+        etc1_uint32 stride) {
+    etc1_byte block[std::max({ETC1_DECODED_BLOCK_SIZE,
+                              ETC2_DECODED_RGB8A1_BLOCK_SIZE,
+                              EAC_DECODED_R11_BLOCK_SIZE,
+                              EAC_DECODED_RG11_BLOCK_SIZE})];
+    etc1_byte alphaBlock[EAC_DECODED_ALPHA_BLOCK_SIZE];
+
+    etc1_uint32 encodedWidth = (width + 3) & ~3;
+    etc1_uint32 encodedHeight = (height + 3) & ~3;
+
+    int pixelSize = etc_get_decoded_pixel_size(format);
+    bool isSigned = (format == EtcSignedR11 || format == EtcSignedRG11);
+
+    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
+        etc1_uint32 yEnd = height - y;
+        if (yEnd > 4) {
+            yEnd = 4;
+        }
+        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
+            etc1_uint32 xEnd = width - x;
+            if (xEnd > 4) {
+                xEnd = 4;
+            }
+            switch (format) {
+                case EtcRGBA8:
+                    eac_decode_single_channel_block(pIn, 1, false, alphaBlock);
+                    pIn += EAC_ENCODE_ALPHA_BLOCK_SIZE;
+                    // Do not break
+                    // Fall through to EtcRGB8 to decode the RGB part
+                    [[fallthrough]];
+                case EtcRGB8:
+                    etc2_decode_rgb_block(pIn, false, block);
+                    pIn += ETC1_ENCODED_BLOCK_SIZE;
+                    break;
+                case EtcRGB8A1:
+                    etc2_decode_rgb_block(pIn, true, block);
+                    pIn += ETC1_ENCODED_BLOCK_SIZE;
+                    break;
+                case EtcR11:
+                case EtcSignedR11:
+                    eac_decode_single_channel_block(pIn, 4, isSigned, block);
+                    pIn += EAC_ENCODE_R11_BLOCK_SIZE;
+                    break;
+                case EtcRG11:
+                case EtcSignedRG11:
+                    // r channel
+                    eac_decode_single_channel_block(pIn, 4, isSigned, block);
+                    pIn += EAC_ENCODE_R11_BLOCK_SIZE;
+                    // g channel
+                    eac_decode_single_channel_block(pIn, 4, isSigned,
+                            block + EAC_DECODED_R11_BLOCK_SIZE);
+                    pIn += EAC_ENCODE_R11_BLOCK_SIZE;
+                    break;
+                default:
+                    assert(0);
+            }
+            for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
+                etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
+                switch (format) {
+                    case EtcRGB8:
+                    case EtcRGB8A1:
+                    case EtcR11:
+                    case EtcSignedR11: {
+                            const etc1_byte* q = block + (cy * 4) * pixelSize;
+                            memcpy(p, q, xEnd * pixelSize);
+                        }
+                        break;
+                    case EtcRG11:
+                    case EtcSignedRG11: {
+                            const etc1_byte* r = block + cy * EAC_DECODED_R11_BLOCK_SIZE / 4;
+                            const etc1_byte* g = block + cy * EAC_DECODED_R11_BLOCK_SIZE / 4 + EAC_DECODED_R11_BLOCK_SIZE;
+                            int channelSize = pixelSize / 2;
+                            for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
+                                memcpy(p, r, channelSize);
+                                p += channelSize;
+                                r += channelSize;
+                                memcpy(p, g, channelSize);
+                                p += channelSize;
+                                g += channelSize;
+                            }
+                        }
+                        break;
+                    case EtcRGBA8: {
+                            const etc1_byte* q = block + (cy * 4) * 3;
+                            const etc1_byte* qa = alphaBlock + cy * 4;
+                            for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
+                                // copy rgb data
+                                memcpy(p, q, 3);
+                                p += 3;
+                                q += 3;
+                                *p++ = *qa++;
+                            }
+                        }
+                        break;
+                    default:
+                        assert(0);
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
+
+static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
+static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
+static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
+static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
+static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
+
+static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
+
+static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
+    pOut[0] = (etc1_byte) (data >> 8);
+    pOut[1] = (etc1_byte) data;
+}
+
+static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
+    return (pIn[0] << 8) | pIn[1];
+}
+
+// Format a PKM header
+
+void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
+    memcpy(pHeader, kMagic, sizeof(kMagic));
+    etc1_uint32 encodedWidth = (width + 3) & ~3;
+    etc1_uint32 encodedHeight = (height + 3) & ~3;
+    writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
+    writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
+    writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
+    writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
+    writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
+}
+
+// Check if a PKM header is correctly formatted.
+
+etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
+    if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
+        return false;
+    }
+    etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
+    etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
+    etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
+    etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
+    etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
+    return format == ETC1_RGB_NO_MIPMAPS &&
+            encodedWidth >= width && encodedWidth - width < 4 &&
+            encodedHeight >= height && encodedHeight - height < 4;
+}
+
+// Read the image width from a PKM header
+
+etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
+    return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
+}
+
+// Read the image height from a PKM header
+
+etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
+    return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
+}
diff --git a/shared/OpenglCodecCommon/etc.h b/shared/OpenglCodecCommon/etc.h
new file mode 100644
index 0000000..7d43c89
--- /dev/null
+++ b/shared/OpenglCodecCommon/etc.h
@@ -0,0 +1,130 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#define MAX_ETC_SUPPORTED 12
+
+#define ETC1_ENCODED_BLOCK_SIZE 8
+#define ETC1_DECODED_BLOCK_SIZE 48
+#define ETC2_DECODED_RGB8A1_BLOCK_SIZE 64
+#define EAC_ENCODE_ALPHA_BLOCK_SIZE 8
+#define EAC_DECODED_ALPHA_BLOCK_SIZE 16
+#define EAC_ENCODE_R11_BLOCK_SIZE 8
+#define EAC_DECODED_R11_BLOCK_SIZE 64
+#define EAC_DECODED_RG11_BLOCK_SIZE (EAC_DECODED_R11_BLOCK_SIZE*2)
+
+#ifndef ETC1_RGB8_OES
+#define ETC1_RGB8_OES 0x8D64
+#endif
+
+typedef unsigned char etc1_byte;
+typedef int etc1_bool;
+typedef unsigned int etc1_uint32;
+
+enum ETC2ImageFormat {
+    EtcRGB8, EtcRGBA8, EtcR11, EtcSignedR11, EtcRG11, EtcSignedRG11, EtcRGB8A1
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Encode a block of pixels.
+//
+// pIn is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
+// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
+// value of pixel (x, y).
+//
+// validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
+// the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
+//
+// pOut is an ETC1 compressed version of the data.
+
+void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 validPixelMask, etc1_byte* pOut);
+
+// Decode a block of rgb pixels.
+//
+// pIn is an ETC2 compressed version of the data.
+//
+// pOut is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
+// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
+// value of pixel (x, y).
+
+void etc2_decode_rgb_block(const etc1_byte* pIn, bool isPunchthroughAlpha, etc1_byte* pOut);
+
+// Decode a block of single channel pixels
+// This is used when decoding the alpha channel of RGBA8_ETC2_EAC format, or
+// when decoding R11_EAC format
+// decodedElementBytes: number of bits per element after decoding.
+// For RGBA8_ETC2_EAC it must be 1, for R11_EAC it must be 2
+
+void eac_decode_single_channel_block(const etc1_byte* pIn,
+                                     int decodedElementBytes, bool isSigned,
+                                     etc1_byte* pOut);
+
+// Return the size of the encoded image data (does not include size of PKM header).
+
+etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height);
+
+etc1_uint32 etc_get_encoded_data_size(ETC2ImageFormat format, etc1_uint32 width,
+                                      etc1_uint32 height);
+etc1_uint32 etc_get_decoded_pixel_size(ETC2ImageFormat format);
+
+// Encode an entire image.
+// pIn - pointer to the image data. Formatted such that
+//       pixel (x,y) is at pIn + pixelSize * x + stride * y;
+// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
+// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image.
+// returns non-zero if there is an error.
+
+int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
+        etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut);
+
+// Decode an entire image.
+// pIn - pointer to encoded data.
+// pOut - pointer to the image data. Will be written such that
+//        pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
+//        large enough to store entire image.
+//        (pixelSize=3 for rgb images, pixelSize=4 for images with alpha channel)
+// returns non-zero if there is an error.
+
+int etc2_decode_image(const etc1_byte* pIn, ETC2ImageFormat format,
+        etc1_byte* pOut,
+        etc1_uint32 width, etc1_uint32 height,
+        etc1_uint32 stride);
+
+// Size of a PKM header, in bytes.
+
+#define ETC_PKM_HEADER_SIZE 16
+
+// Format a PKM header
+
+void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height);
+
+// Check if a PKM header is correctly formatted.
+
+etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader);
+
+// Read the image width from a PKM header
+
+etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader);
+
+// Read the image height from a PKM header
+
+etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/shared/OpenglCodecCommon/glUtils.cpp b/shared/OpenglCodecCommon/glUtils.cpp
index a0ec240..de62139 100644
--- a/shared/OpenglCodecCommon/glUtils.cpp
+++ b/shared/OpenglCodecCommon/glUtils.cpp
@@ -20,6 +20,273 @@
 
 #include <GLES3/gl31.h>
 
+bool isSamplerType(GLenum type) {
+    switch (type) {
+        case GL_SAMPLER_2D:
+        case GL_SAMPLER_3D:
+        case GL_SAMPLER_CUBE:
+        case GL_SAMPLER_2D_SHADOW:
+        case GL_SAMPLER_2D_ARRAY:
+        case GL_SAMPLER_2D_ARRAY_SHADOW:
+        case GL_SAMPLER_2D_MULTISAMPLE:
+        case GL_SAMPLER_CUBE_SHADOW:
+        case GL_INT_SAMPLER_2D:
+        case GL_INT_SAMPLER_3D:
+        case GL_INT_SAMPLER_CUBE:
+        case GL_INT_SAMPLER_2D_ARRAY:
+        case GL_INT_SAMPLER_2D_MULTISAMPLE:
+        case GL_UNSIGNED_INT_SAMPLER_2D:
+        case GL_UNSIGNED_INT_SAMPLER_3D:
+        case GL_UNSIGNED_INT_SAMPLER_CUBE:
+        case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+        case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool isIntegerType(GLenum type) {
+    switch (type) {
+        case GL_UNSIGNED_INT:
+        case GL_INT:
+        case GL_INT_VEC2:
+        case GL_UNSIGNED_INT_VEC2:
+        case GL_INT_VEC3:
+        case GL_UNSIGNED_INT_VEC3:
+        case GL_INT_VEC4:
+        case GL_UNSIGNED_INT_VEC4:
+        case GL_INT_IMAGE_2D:
+        case GL_INT_IMAGE_3D:
+        case GL_INT_IMAGE_CUBE:
+        case GL_INT_IMAGE_2D_ARRAY:
+        case GL_UNSIGNED_INT_IMAGE_2D:
+        case GL_UNSIGNED_INT_IMAGE_3D:
+        case GL_UNSIGNED_INT_IMAGE_CUBE:
+        case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+        case GL_UNSIGNED_INT_ATOMIC_COUNTER:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool isUnsignedIntType(GLenum type) {
+    switch (type) {
+        case GL_UNSIGNED_INT:
+        case GL_UNSIGNED_INT_VEC2:
+        case GL_UNSIGNED_INT_VEC3:
+        case GL_UNSIGNED_INT_VEC4:
+        case GL_UNSIGNED_INT_IMAGE_2D:
+        case GL_UNSIGNED_INT_IMAGE_3D:
+        case GL_UNSIGNED_INT_IMAGE_CUBE:
+        case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+        case GL_UNSIGNED_INT_ATOMIC_COUNTER:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool isBoolType(GLenum type) {
+    switch (type) {
+        case GL_BOOL:
+        case GL_BOOL_VEC2:
+        case GL_BOOL_VEC3:
+        case GL_BOOL_VEC4:
+            return true;
+        default:
+            return false;
+    }
+}
+
+uint32_t getColumnsOfType(GLenum type) {
+    switch (type) {
+    case GL_BYTE:
+    case GL_UNSIGNED_BYTE:
+    case GL_SHORT:
+    case GL_UNSIGNED_SHORT:
+    case GL_HALF_FLOAT:
+    case GL_HALF_FLOAT_OES:
+    case GL_IMAGE_2D:
+    case GL_IMAGE_3D:
+    case GL_UNSIGNED_INT:
+    case GL_INT:
+    case GL_FLOAT:
+    case GL_FIXED:
+    case GL_BOOL:
+        return 1;
+#ifdef GL_DOUBLE
+    case GL_DOUBLE:
+        return 1;
+    case GL_DOUBLE_VEC2:
+    case GL_DOUBLE_MAT2:
+    case GL_DOUBLE_MAT2x3:
+    case GL_DOUBLE_MAT2x4:
+        return 2;
+    case GL_DOUBLE_VEC3:
+    case GL_DOUBLE_MAT3:
+    case GL_DOUBLE_MAT3x2:
+    case GL_DOUBLE_MAT3x4:
+        return 3;
+    case GL_DOUBLE_VEC4:
+    case GL_DOUBLE_MAT4:
+    case GL_DOUBLE_MAT4x2:
+    case GL_DOUBLE_MAT4x3:
+        return 4;
+#endif
+    case GL_FLOAT_VEC2:
+    case GL_INT_VEC2:
+    case GL_UNSIGNED_INT_VEC2:
+    case GL_BOOL_VEC2:
+    case GL_FLOAT_MAT2:
+    case GL_FLOAT_MAT2x3:
+    case GL_FLOAT_MAT2x4:
+        return 2;
+    case GL_INT_VEC3:
+    case GL_UNSIGNED_INT_VEC3:
+    case GL_BOOL_VEC3:
+    case GL_FLOAT_VEC3:
+    case GL_FLOAT_MAT3:
+    case GL_FLOAT_MAT3x2:
+    case GL_FLOAT_MAT3x4:
+        return 3;
+    case GL_FLOAT_VEC4:
+    case GL_BOOL_VEC4:
+    case GL_INT_VEC4:
+    case GL_UNSIGNED_INT_VEC4:
+    case GL_FLOAT_MAT4:
+    case GL_FLOAT_MAT4x2:
+    case GL_FLOAT_MAT4x3:
+        return 4;
+    case GL_SAMPLER_2D:
+    case GL_SAMPLER_3D:
+    case GL_SAMPLER_CUBE:
+    case GL_SAMPLER_2D_SHADOW:
+    case GL_SAMPLER_2D_ARRAY:
+    case GL_SAMPLER_2D_ARRAY_SHADOW:
+    case GL_SAMPLER_2D_MULTISAMPLE:
+    case GL_SAMPLER_CUBE_SHADOW:
+    case GL_INT_SAMPLER_2D:
+    case GL_INT_SAMPLER_3D:
+    case GL_INT_SAMPLER_CUBE:
+    case GL_INT_SAMPLER_2D_ARRAY:
+    case GL_INT_SAMPLER_2D_MULTISAMPLE:
+    case GL_UNSIGNED_INT_SAMPLER_2D:
+    case GL_UNSIGNED_INT_SAMPLER_3D:
+    case GL_UNSIGNED_INT_SAMPLER_CUBE:
+    case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+    case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+    case GL_IMAGE_CUBE:
+    case GL_IMAGE_2D_ARRAY:
+    case GL_INT_IMAGE_2D:
+    case GL_INT_IMAGE_3D:
+    case GL_INT_IMAGE_CUBE:
+    case GL_INT_IMAGE_2D_ARRAY:
+    case GL_UNSIGNED_INT_IMAGE_2D:
+    case GL_UNSIGNED_INT_IMAGE_3D:
+    case GL_UNSIGNED_INT_IMAGE_CUBE:
+    case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+    case GL_UNSIGNED_INT_ATOMIC_COUNTER:
+    default:
+        return 1;
+    }
+}
+
+uint32_t getRowsOfType(GLenum type) {
+    switch (type) {
+    case GL_BYTE:
+    case GL_UNSIGNED_BYTE:
+    case GL_SHORT:
+    case GL_UNSIGNED_SHORT:
+    case GL_HALF_FLOAT:
+    case GL_HALF_FLOAT_OES:
+    case GL_IMAGE_2D:
+    case GL_IMAGE_3D:
+    case GL_UNSIGNED_INT:
+    case GL_INT:
+    case GL_FLOAT:
+    case GL_FIXED:
+    case GL_BOOL:
+        return 1;
+#ifdef GL_DOUBLE
+    case GL_DOUBLE:
+    case GL_DOUBLE_VEC2:
+    case GL_DOUBLE_VEC3:
+    case GL_DOUBLE_VEC4:
+        return 1;
+    case GL_DOUBLE_MAT2:
+    case GL_DOUBLE_MAT3x2:
+    case GL_DOUBLE_MAT4x2:
+        return 2;
+    case GL_DOUBLE_MAT3:
+    case GL_DOUBLE_MAT2x3:
+    case GL_DOUBLE_MAT4x3:
+        return 3;
+    case GL_DOUBLE_MAT4:
+    case GL_DOUBLE_MAT3x4:
+    case GL_DOUBLE_MAT2x4:
+        return 4;
+#endif
+    case GL_FLOAT_VEC2:
+    case GL_INT_VEC2:
+    case GL_UNSIGNED_INT_VEC2:
+    case GL_BOOL_VEC2:
+    case GL_INT_VEC3:
+    case GL_UNSIGNED_INT_VEC3:
+    case GL_BOOL_VEC3:
+    case GL_FLOAT_VEC3:
+    case GL_FLOAT_VEC4:
+    case GL_BOOL_VEC4:
+    case GL_INT_VEC4:
+    case GL_UNSIGNED_INT_VEC4:
+        return 1;
+    case GL_FLOAT_MAT2:
+    case GL_FLOAT_MAT3x2:
+    case GL_FLOAT_MAT4x2:
+        return 2;
+    case GL_FLOAT_MAT3:
+    case GL_FLOAT_MAT2x3:
+    case GL_FLOAT_MAT4x3:
+        return 3;
+    case GL_FLOAT_MAT4:
+    case GL_FLOAT_MAT2x4:
+    case GL_FLOAT_MAT3x4:
+        return 4;
+    case GL_SAMPLER_2D:
+    case GL_SAMPLER_3D:
+    case GL_SAMPLER_CUBE:
+    case GL_SAMPLER_2D_SHADOW:
+    case GL_SAMPLER_2D_ARRAY:
+    case GL_SAMPLER_2D_ARRAY_SHADOW:
+    case GL_SAMPLER_2D_MULTISAMPLE:
+    case GL_SAMPLER_CUBE_SHADOW:
+    case GL_INT_SAMPLER_2D:
+    case GL_INT_SAMPLER_3D:
+    case GL_INT_SAMPLER_CUBE:
+    case GL_INT_SAMPLER_2D_ARRAY:
+    case GL_INT_SAMPLER_2D_MULTISAMPLE:
+    case GL_UNSIGNED_INT_SAMPLER_2D:
+    case GL_UNSIGNED_INT_SAMPLER_3D:
+    case GL_UNSIGNED_INT_SAMPLER_CUBE:
+    case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+    case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+    case GL_IMAGE_CUBE:
+    case GL_IMAGE_2D_ARRAY:
+    case GL_INT_IMAGE_2D:
+    case GL_INT_IMAGE_3D:
+    case GL_INT_IMAGE_CUBE:
+    case GL_INT_IMAGE_2D_ARRAY:
+    case GL_UNSIGNED_INT_IMAGE_2D:
+    case GL_UNSIGNED_INT_IMAGE_3D:
+    case GL_UNSIGNED_INT_IMAGE_CUBE:
+    case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+    case GL_UNSIGNED_INT_ATOMIC_COUNTER:
+    default:
+        return 1;
+    }
+}
+
 size_t glSizeof(GLenum type)
 {
     size_t retval = 0;
@@ -47,6 +314,36 @@
     case GL_DOUBLE:
         retval = 8;
         break;
+    case GL_DOUBLE_VEC2:
+        retval = 16;
+        break;
+    case GL_DOUBLE_VEC3:
+        retval = 24;
+        break;
+    case GL_DOUBLE_VEC4:
+        retval = 32;
+        break;
+    case GL_DOUBLE_MAT2:
+        retval = 8 * 4;
+        break;
+    case GL_DOUBLE_MAT3:
+        retval = 8 * 9;
+        break;
+    case GL_DOUBLE_MAT4:
+        retval = 8 * 16;
+        break;
+    case GL_DOUBLE_MAT2x3:
+    case GL_DOUBLE_MAT3x2:
+        retval = 8 * 6;
+        break;
+    case GL_DOUBLE_MAT2x4:
+    case GL_DOUBLE_MAT4x2:
+        retval = 8 * 8;
+        break;
+    case GL_DOUBLE_MAT3x4:
+    case GL_DOUBLE_MAT4x3:
+        retval = 8 * 12;
+        break;
 #endif
     case GL_FLOAT_VEC2:
     case GL_INT_VEC2:
@@ -88,6 +385,32 @@
     case GL_SAMPLER_2D:
     case GL_SAMPLER_3D:
     case GL_SAMPLER_CUBE:
+    case GL_SAMPLER_2D_SHADOW:
+    case GL_SAMPLER_2D_ARRAY:
+    case GL_SAMPLER_2D_ARRAY_SHADOW:
+    case GL_SAMPLER_2D_MULTISAMPLE:
+    case GL_SAMPLER_CUBE_SHADOW:
+    case GL_INT_SAMPLER_2D:
+    case GL_INT_SAMPLER_3D:
+    case GL_INT_SAMPLER_CUBE:
+    case GL_INT_SAMPLER_2D_ARRAY:
+    case GL_INT_SAMPLER_2D_MULTISAMPLE:
+    case GL_UNSIGNED_INT_SAMPLER_2D:
+    case GL_UNSIGNED_INT_SAMPLER_3D:
+    case GL_UNSIGNED_INT_SAMPLER_CUBE:
+    case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+    case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+    case GL_IMAGE_CUBE:
+    case GL_IMAGE_2D_ARRAY:
+    case GL_INT_IMAGE_2D:
+    case GL_INT_IMAGE_3D:
+    case GL_INT_IMAGE_CUBE:
+    case GL_INT_IMAGE_2D_ARRAY:
+    case GL_UNSIGNED_INT_IMAGE_2D:
+    case GL_UNSIGNED_INT_IMAGE_3D:
+    case GL_UNSIGNED_INT_IMAGE_CUBE:
+    case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+    case GL_UNSIGNED_INT_ATOMIC_COUNTER:
         retval = 4;
         break;
     case GL_UNSIGNED_SHORT_4_4_4_4:
@@ -669,3 +992,92 @@
     }
     return 4;
 }
+
+bool colorRenderableFormat(GLint internalformat, GLenum texturetype, int majorVersion, int minorVersion, bool hasColorBufferFloatExtension, bool hasColorBufferHalfFloatExtension) {
+    switch (internalformat) {
+        case GL_RGB:
+        case GL_RGBA:
+        case GL_BGRA_EXT:
+            switch (texturetype) {
+                case GL_FLOAT:
+                case GL_HALF_FLOAT_OES:
+                case GL_UNSIGNED_INT_10F_11F_11F_REV:
+                case GL_UNSIGNED_INT_2_10_10_10_REV:
+                    return false;
+                default:
+                    return true;
+            }
+            break;
+        case GL_R8:
+        case GL_RG8:
+        case GL_RGB8:
+        case GL_RGB565:
+        case GL_RGBA4:
+        case GL_RGB5_A1:
+        case GL_RGBA8:
+        case GL_RGB10_A2:
+        case GL_RGB10_A2UI:
+        case GL_SRGB8_ALPHA8:
+        case GL_R8I:
+        case GL_R8UI:
+        case GL_R16I:
+        case GL_R16UI:
+        case GL_R32I:
+        case GL_R32UI:
+        case GL_RG8I:
+        case GL_RG8UI:
+        case GL_RG16I:
+        case GL_RG16UI:
+        case GL_RG32I:
+        case GL_RG32UI:
+        case GL_RGBA8I:
+        case GL_RGBA8UI:
+        case GL_RGBA16I:
+        case GL_RGBA16UI:
+        case GL_RGBA32I:
+        case GL_RGBA32UI:
+        case GL_BGRA8_EXT:
+            return true;
+        case GL_R16F:
+        case GL_RG16F:
+        case GL_RGBA16F:
+        case GL_R32F:
+        case GL_RG32F:
+        case GL_RGBA32F:
+        case GL_R11F_G11F_B10F:
+            return majorVersion >= 3 && hasColorBufferFloatExtension;
+        case GL_RGB16F:
+            return majorVersion >= 3 && hasColorBufferHalfFloatExtension;
+        default:
+            return false;
+    }
+}
+
+bool depthRenderableFormat(GLint internalformat) {
+    switch (internalformat) {
+        case GL_DEPTH_STENCIL:
+        case GL_DEPTH_COMPONENT:
+        case GL_DEPTH_COMPONENT16:
+        case GL_DEPTH_COMPONENT24:
+        case GL_DEPTH_COMPONENT32F:
+        case GL_DEPTH24_STENCIL8:
+        case GL_DEPTH32F_STENCIL8:
+        case 0X81A7: // GL_DEPTH_COMPONENT32
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool stencilRenderableFormat(GLint internalformat) {
+    switch (internalformat) {
+        case GL_STENCIL_INDEX:
+        case GL_DEPTH_STENCIL:
+        case GL_STENCIL_INDEX8:
+        case GL_DEPTH24_STENCIL8:
+        case GL_DEPTH32F_STENCIL8:
+            return true;
+        default:
+            return false;
+    }
+}
diff --git a/shared/OpenglCodecCommon/glUtils.h b/shared/OpenglCodecCommon/glUtils.h
index 2f20b68..489ac18 100644
--- a/shared/OpenglCodecCommon/glUtils.h
+++ b/shared/OpenglCodecCommon/glUtils.h
@@ -57,6 +57,12 @@
     INDIRECT_COMMAND_DRAWELEMENTS = 1,
 } IndirectCommandType;
 
+    bool isSamplerType(GLenum type);
+    bool isIntegerType(GLenum type);
+    bool isUnsignedIntType(GLenum type);
+    bool isBoolType(GLenum type);
+    uint32_t getColumnsOfType(GLenum type);
+    uint32_t getRowsOfType(GLenum type);
     size_t glSizeof(GLenum type);
     size_t glUtilsParamSize(GLenum param);
     void   glUtilsPackPointerData(unsigned char *dst, unsigned char *str,
@@ -73,6 +79,11 @@
 
     GLuint glUtilsIndirectStructSize(IndirectCommandType cmdType);
 
+    bool colorRenderableFormat(GLint internalformat, GLenum texturetype, int majorVersion, int minorVersion, bool hasColorBufferFloatExtension, bool hasColorBufferHalfFloatExtension);
+
+    bool depthRenderableFormat(GLint internalformat);
+    bool stencilRenderableFormat(GLint internalformat);
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/system/GLESv1/CMakeLists.txt b/system/GLESv1/CMakeLists.txt
index 307941d..8143b9a 100644
--- a/system/GLESv1/CMakeLists.txt
+++ b/system/GLESv1/CMakeLists.txt
@@ -4,7 +4,7 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/GLESv1/Android.mk" "e095cb082e3791719749cfc80b90560afd7348eb0d7895449d2509aa129bea75")
 set(GLESv1_CM_emulation_src gl.cpp)
 android_add_library(TARGET GLESv1_CM_emulation SHARED LICENSE Apache-2.0 SRC gl.cpp)
-target_include_directories(GLESv1_CM_emulation PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+target_include_directories(GLESv1_CM_emulation PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(GLESv1_CM_emulation PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"GLES_emulation\"")
 target_compile_options(GLESv1_CM_emulation PRIVATE "-fvisibility=default" "-Wno-unused-parameter")
-target_link_libraries(GLESv1_CM_emulation PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui androidemu cutils utils log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
+target_link_libraries(GLESv1_CM_emulation PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host cutils utils androidemu PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
diff --git a/system/GLESv1_enc/CMakeLists.txt b/system/GLESv1_enc/CMakeLists.txt
index d23b1a3..2fb5b03 100644
--- a/system/GLESv1_enc/CMakeLists.txt
+++ b/system/GLESv1_enc/CMakeLists.txt
@@ -4,7 +4,7 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc/Android.mk" "953e6b7371d10eed63a4be555f8f1fb6f347338484a78102fa8f55dff96f5d3b")
 set(GLESv1_enc_src GLEncoder.cpp GLEncoderUtils.cpp gl_client_context.cpp gl_enc.cpp gl_entry.cpp)
 android_add_library(TARGET GLESv1_enc SHARED LICENSE Apache-2.0 SRC GLEncoder.cpp GLEncoderUtils.cpp gl_client_context.cpp gl_enc.cpp gl_entry.cpp)
-target_include_directories(GLESv1_enc PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+target_include_directories(GLESv1_enc PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(GLESv1_enc PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"emuglGLESv1_enc\"")
 target_compile_options(GLESv1_enc PRIVATE "-fvisibility=default" "-Wno-unused-parameter")
-target_link_libraries(GLESv1_enc PRIVATE OpenglCodecCommon_host cutils utils log android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
+target_link_libraries(GLESv1_enc PRIVATE OpenglCodecCommon_host cutils utils log androidemu android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
diff --git a/system/GLESv1_enc/GLEncoder.cpp b/system/GLESv1_enc/GLEncoder.cpp
index 20f2c02..1ab13e3 100644
--- a/system/GLESv1_enc/GLEncoder.cpp
+++ b/system/GLESv1_enc/GLEncoder.cpp
@@ -989,7 +989,7 @@
     GLEncoder* ctx = (GLEncoder*)self;
     GLClientState* state = ctx->m_state;
 
-    state->attachTextureObject(target, attachment, texture);
+    state->attachTextureObject(target, attachment, texture, level, 0);
 
     ctx->m_glFramebufferTexture2DOES_enc(self, target, attachment, textarget, texture, level);
 }
@@ -1000,7 +1000,7 @@
     GLEncoder* ctx = (GLEncoder*)self;
     GLClientState* state = ctx->m_state;
 
-    state->attachTextureObject(target, attachment, texture);
+    state->attachTextureObject(target, attachment, texture, level, 0);
 
     ctx->m_glFramebufferTexture2DMultisampleIMG_enc(self, target, attachment, textarget, texture, level, samples);
 }
diff --git a/system/GLESv2/CMakeLists.txt b/system/GLESv2/CMakeLists.txt
index 17114b5..b04a4bb 100644
--- a/system/GLESv2/CMakeLists.txt
+++ b/system/GLESv2/CMakeLists.txt
@@ -4,7 +4,7 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/GLESv2/Android.mk" "d8f9dda69ec57ad8b7a65f02c3335b16a4724f612dec1d1a2cd793c28c0a10f9")
 set(GLESv2_emulation_src gl2.cpp)
 android_add_library(TARGET GLESv2_emulation SHARED LICENSE Apache-2.0 SRC gl2.cpp)
-target_include_directories(GLESv2_emulation PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+target_include_directories(GLESv2_emulation PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(GLESv2_emulation PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"GLESv2_emulation\"")
 target_compile_options(GLESv2_emulation PRIVATE "-fvisibility=default" "-Wno-unused-parameter")
-target_link_libraries(GLESv2_emulation PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui androidemu cutils utils log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
+target_link_libraries(GLESv2_emulation PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host cutils utils androidemu PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
diff --git a/system/GLESv2/gl2.cpp b/system/GLESv2/gl2.cpp
index a36bdd3..1c5ece7 100644
--- a/system/GLESv2/gl2.cpp
+++ b/system/GLESv2/gl2.cpp
@@ -101,6 +101,7 @@
     DBG("glEGLImageTargetRenderbufferStorageOES v2 image=%p\n", img);
     //TODO: check error - we don't have a way to set gl error
     EGLImage_t *image = (EGLImage_t*)img;
+    GLeglImageOES hostImage = reinterpret_cast<GLeglImageOES>((intptr_t)image->host_egl_image);
 
     if (image->target == EGL_NATIVE_BUFFER_ANDROID) {
         android_native_buffer_t* native_buffer = ((EGLImage_t*)image)->native_buffer;
@@ -114,6 +115,8 @@
         }
 
         DEFINE_AND_VALIDATE_HOST_CONNECTION();
+        GET_CONTEXT;
+        ctx->associateEGLImage(target, hostImage);
         rcEnc->rcBindRenderbuffer(rcEnc,
                 grallocHelper->getHostHandle(native_buffer->handle));
     } else {
diff --git a/system/GLESv2_enc/CMakeLists.txt b/system/GLESv2_enc/CMakeLists.txt
index 44e9fe9..5d0f049 100644
--- a/system/GLESv2_enc/CMakeLists.txt
+++ b/system/GLESv2_enc/CMakeLists.txt
@@ -4,7 +4,7 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc/Android.mk" "df543672d1f36e43fb783b08200aa85dbf3a2e7167f8ecd7e4c01c80e6fd1650")
 set(GLESv2_enc_src GL2EncoderUtils.cpp GL2Encoder.cpp GLESv2Validation.cpp gl2_client_context.cpp gl2_enc.cpp gl2_entry.cpp IOStream2.cpp)
 android_add_library(TARGET GLESv2_enc SHARED LICENSE Apache-2.0 SRC GL2EncoderUtils.cpp GL2Encoder.cpp GLESv2Validation.cpp gl2_client_context.cpp gl2_enc.cpp gl2_entry.cpp IOStream2.cpp)
-target_include_directories(GLESv2_enc PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+target_include_directories(GLESv2_enc PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(GLESv2_enc PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"emuglGLESv2_enc\"")
 target_compile_options(GLESv2_enc PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-unused-private-field")
-target_link_libraries(GLESv2_enc PRIVATE OpenglCodecCommon_host cutils utils log android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
+target_link_libraries(GLESv2_enc PRIVATE OpenglCodecCommon_host cutils utils log androidemu android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
diff --git a/system/GLESv2_enc/GL2Encoder.cpp b/system/GLESv2_enc/GL2Encoder.cpp
index a770979..919fdef 100755
--- a/system/GLESv2_enc/GL2Encoder.cpp
+++ b/system/GLESv2_enc/GL2Encoder.cpp
@@ -16,6 +16,7 @@
 
 #include "GL2Encoder.h"
 #include "GLESv2Validation.h"
+#include "GLESTextureUtils.h"
 
 #include <string>
 #include <map>
@@ -81,6 +82,7 @@
     m_num_compressedTextureFormats = 0;
     m_max_combinedTextureImageUnits = 0;
     m_max_vertexTextureImageUnits = 0;
+    m_max_array_texture_layers = 0;
     m_max_textureImageUnits = 0;
     m_max_cubeMapTextureSize = 0;
     m_max_renderBufferSize = 0;
@@ -296,6 +298,7 @@
     OVERRIDE(glGenerateMipmap);
 
     OVERRIDE(glBindSampler);
+    OVERRIDE(glDeleteSamplers);
 
     OVERRIDE_CUSTOM(glFenceSync);
     OVERRIDE_CUSTOM(glClientWaitSync);
@@ -378,6 +381,78 @@
 
     OVERRIDE(glDispatchCompute);
     OVERRIDE(glDispatchComputeIndirect);
+
+    OVERRIDE(glGenTransformFeedbacks);
+    OVERRIDE(glDeleteTransformFeedbacks);
+    OVERRIDE(glGenSamplers);
+    OVERRIDE(glGenQueries);
+    OVERRIDE(glDeleteQueries);
+
+    OVERRIDE(glBindTransformFeedback);
+    OVERRIDE(glBeginQuery);
+    OVERRIDE(glEndQuery);
+
+    OVERRIDE(glClear);
+    OVERRIDE(glClearBufferfi);
+    OVERRIDE(glCopyTexSubImage2D);
+    OVERRIDE(glCopyTexSubImage3D);
+    OVERRIDE(glCompileShader);
+    OVERRIDE(glValidateProgram);
+    OVERRIDE(glProgramBinary);
+
+    OVERRIDE(glGetSamplerParameterfv);
+    OVERRIDE(glGetSamplerParameteriv);
+    OVERRIDE(glSamplerParameterf);
+    OVERRIDE(glSamplerParameteri);
+    OVERRIDE(glSamplerParameterfv);
+    OVERRIDE(glSamplerParameteriv);
+
+    OVERRIDE(glGetAttribLocation);
+
+    OVERRIDE(glBindAttribLocation);
+    OVERRIDE(glUniformBlockBinding);
+    OVERRIDE(glGetTransformFeedbackVarying);
+    OVERRIDE(glScissor);
+    OVERRIDE(glDepthFunc);
+    OVERRIDE(glViewport);
+    OVERRIDE(glStencilFunc);
+    OVERRIDE(glStencilFuncSeparate);
+    OVERRIDE(glStencilOp);
+    OVERRIDE(glStencilOpSeparate);
+    OVERRIDE(glStencilMaskSeparate);
+    OVERRIDE(glBlendEquation);
+    OVERRIDE(glBlendEquationSeparate);
+    OVERRIDE(glBlendFunc);
+    OVERRIDE(glBlendFuncSeparate);
+    OVERRIDE(glCullFace);
+    OVERRIDE(glFrontFace);
+    OVERRIDE(glLineWidth);
+    OVERRIDE(glVertexAttrib1f);
+    OVERRIDE(glVertexAttrib2f);
+    OVERRIDE(glVertexAttrib3f);
+    OVERRIDE(glVertexAttrib4f);
+    OVERRIDE(glVertexAttrib1fv);
+    OVERRIDE(glVertexAttrib2fv);
+    OVERRIDE(glVertexAttrib3fv);
+    OVERRIDE(glVertexAttrib4fv);
+    OVERRIDE(glVertexAttribI4i);
+    OVERRIDE(glVertexAttribI4ui);
+    OVERRIDE(glVertexAttribI4iv);
+    OVERRIDE(glVertexAttribI4uiv);
+
+    OVERRIDE(glGetShaderPrecisionFormat);
+    OVERRIDE(glGetProgramiv);
+    OVERRIDE(glGetActiveUniform);
+    OVERRIDE(glGetActiveUniformsiv);
+    OVERRIDE(glGetActiveUniformBlockName);
+    OVERRIDE(glGetActiveAttrib);
+    OVERRIDE(glGetRenderbufferParameteriv);
+    OVERRIDE(glGetQueryiv);
+    OVERRIDE(glGetQueryObjectuiv);
+    OVERRIDE(glIsEnabled);
+    OVERRIDE(glHint);
+
+    OVERRIDE(glGetFragDataLocation);
 }
 
 GL2Encoder::~GL2Encoder()
@@ -390,7 +465,9 @@
     GL2Encoder *ctx = (GL2Encoder *)self;
     GLenum err = ctx->getError();
     if(err != GL_NO_ERROR) {
-        ctx->m_glGetError_enc(ctx); // also clear host error
+        if (!ctx->m_noHostError) {
+            ctx->m_glGetError_enc(ctx); // also clear host error
+        }
         ctx->setError(GL_NO_ERROR);
         return err;
     }
@@ -408,6 +485,9 @@
         mCtx(ctx),
         guest_error(ctx->getError()),
         host_error(ctx->m_glGetError_enc(ctx)) {
+            if (ctx->m_noHostError) {
+                host_error = GL_NO_ERROR;
+            }
             // Preserve any existing GL error in the guest:
             // OpenGL ES 3.0.5 spec:
             // The command enum GetError( void ); is used to obtain error information.
@@ -580,6 +660,7 @@
     GLuint bufferId = ctx->m_state->getBuffer(target);
     SET_ERROR_IF(bufferId==0, GL_INVALID_OPERATION);
     SET_ERROR_IF(size<0, GL_INVALID_VALUE);
+    SET_ERROR_IF(!GLESv2Validation::bufferUsage(ctx, usage), GL_INVALID_ENUM);
 
     ctx->m_shared->updateBufferData(bufferId, size, data);
     ctx->m_shared->setBufferUsage(bufferId, usage);
@@ -623,18 +704,8 @@
     }
 }
 
-static bool isValidVertexAttribIndex(void *self, GLuint indx)
-{
-    GL2Encoder *ctx = (GL2Encoder *)self;
-    GLint maxIndex;
-    ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
-    return indx < maxIndex;
-}
-
 #define VALIDATE_VERTEX_ATTRIB_INDEX(index) \
-    SET_ERROR_WITH_MESSAGE_IF( \
-            !isValidVertexAttribIndex(self, index), GL_INVALID_VALUE, \
-            GLESv2Validation::vertexAttribIndexRangeErrorMsg, (ctx, index)); \
+    SET_ERROR_IF(index >= CODEC_MAX_VERTEX_ATTRIBUTES, GL_INVALID_VALUE); \
 
 void GL2Encoder::s_glVertexAttribPointer(void *self, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * ptr)
 {
@@ -722,6 +793,14 @@
             ctx->m_max_vertexTextureImageUnits = *ptr;
         }
         break;
+    case GL_MAX_ARRAY_TEXTURE_LAYERS:
+        if (ctx->m_max_array_texture_layers != 0) {
+            *ptr = ctx->m_max_array_texture_layers;
+        } else {
+            ctx->safe_glGetIntegerv(param, ptr);
+            ctx->m_max_array_texture_layers = *ptr;
+        }
+        break;
     case GL_MAX_TEXTURE_IMAGE_UNITS:
         if (ctx->m_max_textureImageUnits != 0) {
             *ptr = ctx->m_max_textureImageUnits;
@@ -741,10 +820,7 @@
 
     case GL_MAX_VERTEX_ATTRIBS:
         SET_ERROR_IF(!state, GL_INVALID_OPERATION);
-        if (!state->getClientStateParameter<GLint>(param, ptr)) {
-            ctx->safe_glGetIntegerv(param, ptr);
-            state->setMaxVertexAttribs(*ptr);
-        }
+        *ptr = CODEC_MAX_VERTEX_ATTRIBUTES;
         break;
     case GL_MAX_VERTEX_ATTRIB_STRIDE:
         if (ctx->m_max_vertexAttribStride != 0) {
@@ -776,6 +852,13 @@
         } else {
             ctx->safe_glGetIntegerv(param, ptr);
             ctx->m_max_textureSize = *ptr;
+            if (ctx->m_max_textureSize > 0) {
+                uint32_t current = 1;
+                while (current < ctx->m_max_textureSize) {
+                    ++ctx->m_log2MaxTextureSize;
+                    current = current << 1;
+                }
+            }
         }
         break;
     case GL_MAX_3D_TEXTURE_SIZE:
@@ -1049,10 +1132,8 @@
 void GL2Encoder::s_glGetVertexAttribiv(void *self, GLuint index, GLenum pname, GLint *params)
 {
     GL2Encoder *ctx = (GL2Encoder *)self;
-    assert(ctx->m_state);
-    GLint maxIndex;
-    ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
-    SET_ERROR_IF(!(index < maxIndex), GL_INVALID_VALUE);
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
+    SET_ERROR_IF(!GLESv2Validation::allowedGetVertexAttrib(pname), GL_INVALID_ENUM);
 
     if (!ctx->m_state->getVertexAttribParameter<GLint>(index, pname, params)) {
         ctx->m_glGetVertexAttribiv_enc(self, index, pname, params);
@@ -1062,10 +1143,8 @@
 void GL2Encoder::s_glGetVertexAttribfv(void *self, GLuint index, GLenum pname, GLfloat *params)
 {
     GL2Encoder *ctx = (GL2Encoder *)self;
-    assert(ctx->m_state);
-    GLint maxIndex;
-    ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
-    SET_ERROR_IF(!(index < maxIndex), GL_INVALID_VALUE);
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
+    SET_ERROR_IF(!GLESv2Validation::allowedGetVertexAttrib(pname), GL_INVALID_ENUM);
 
     if (!ctx->m_state->getVertexAttribParameter<GLfloat>(index, pname, params)) {
         ctx->m_glGetVertexAttribfv_enc(self, index, pname, params);
@@ -1076,9 +1155,7 @@
 {
     GL2Encoder *ctx = (GL2Encoder *)self;
     if (ctx->m_state == NULL) return;
-    GLint maxIndex;
-    ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
-    SET_ERROR_IF(!(index < maxIndex), GL_INVALID_VALUE);
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
     SET_ERROR_IF(pname != GL_VERTEX_ATTRIB_ARRAY_POINTER, GL_INVALID_ENUM);
     (void)pname;
 
@@ -1327,6 +1404,7 @@
     assert(ctx->m_state != NULL);
     SET_ERROR_IF(!isValidDrawMode(mode), GL_INVALID_ENUM);
     SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(ctx->m_state->checkFramebufferCompleteness(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
 
     bool has_client_vertex_arrays = false;
     bool has_indirect_arrays = false;
@@ -1355,6 +1433,7 @@
     SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(!(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT), GL_INVALID_ENUM);
     SET_ERROR_IF(ctx->m_state->getTransformFeedbackActiveUnpaused(), GL_INVALID_OPERATION);
+    SET_ERROR_IF(ctx->m_state->checkFramebufferCompleteness(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
 
     bool has_client_vertex_arrays = false;
     bool has_indirect_arrays = false;
@@ -1443,6 +1522,7 @@
     assert(ctx->m_state != NULL);
     SET_ERROR_IF(!isValidDrawMode(mode), GL_INVALID_ENUM);
     SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(ctx->m_state->checkFramebufferCompleteness(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
 
     bool has_client_vertex_arrays = false;
     bool has_indirect_arrays = false;
@@ -1470,6 +1550,7 @@
     SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(!(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT), GL_INVALID_ENUM);
     SET_ERROR_IF(ctx->m_state->getTransformFeedbackActiveUnpaused(), GL_INVALID_OPERATION);
+    SET_ERROR_IF(ctx->m_state->checkFramebufferCompleteness(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
 
     bool has_client_vertex_arrays = false;
     bool has_indirect_arrays = false;
@@ -1804,22 +1885,31 @@
     SET_ERROR_IF(!isProgram && !ctx->m_shared->isShader(program), GL_INVALID_VALUE);
     SET_ERROR_IF(!isProgram, GL_INVALID_OPERATION);
 
+    if (program == ctx->m_state->currentProgram() ||
+        (!ctx->m_state->currentProgram() &&
+         (program == ctx->m_state->currentShaderProgram()))) {
+        SET_ERROR_IF(ctx->m_state->getTransformFeedbackActive(), GL_INVALID_OPERATION);
+    }
+
     ctx->m_glLinkProgram_enc(self, program);
 
     GLint linkStatus = 0;
-    ctx->glGetProgramiv(self, program, GL_LINK_STATUS, &linkStatus);
+    ctx->m_glGetProgramiv_enc(self, program, GL_LINK_STATUS, &linkStatus);
+    ctx->m_shared->setProgramLinkStatus(program, linkStatus);
     if (!linkStatus) {
         return;
     }
 
-    //get number of active uniforms in the program
+    // get number of active uniforms and attributes in the program
     GLint numUniforms=0;
-    ctx->glGetProgramiv(self, program, GL_ACTIVE_UNIFORMS, &numUniforms);
-    ctx->m_shared->initProgramData(program,numUniforms);
+    GLint numAttributes=0;
+    ctx->m_glGetProgramiv_enc(self, program, GL_ACTIVE_UNIFORMS, &numUniforms);
+    ctx->m_glGetProgramiv_enc(self, program, GL_ACTIVE_ATTRIBUTES, &numAttributes);
+    ctx->m_shared->initProgramData(program,numUniforms,numAttributes);
 
     //get the length of the longest uniform name
     GLint maxLength=0;
-    ctx->glGetProgramiv(self, program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
+    ctx->m_glGetProgramiv_enc(self, program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
 
     GLint size;
     GLenum type;
@@ -1828,17 +1918,54 @@
     //for each active uniform, get its size and starting location.
     for (GLint i=0 ; i<numUniforms ; ++i)
     {
-        ctx->glGetActiveUniform(self, program, i, maxLength, NULL, &size, &type, name);
+        ctx->m_glGetActiveUniform_enc(self, program, i, maxLength, NULL, &size, &type, name);
         location = ctx->m_glGetUniformLocation_enc(self, program, name);
         ctx->m_shared->setProgramIndexInfo(program, i, location, size, type, name);
     }
 
+    if (ctx->majorVersion() > 2) {
+        GLint numBlocks;
+        ctx->m_glGetProgramiv_enc(ctx, program, GL_ACTIVE_UNIFORM_BLOCKS, &numBlocks);
+        ctx->m_shared->setActiveUniformBlockCountForProgram(program, numBlocks);
+
+        GLint tfVaryingsCount;
+        ctx->m_glGetProgramiv_enc(ctx, program, GL_TRANSFORM_FEEDBACK_VARYINGS, &tfVaryingsCount);
+        ctx->m_shared->setTransformFeedbackVaryingsCountForProgram(program, tfVaryingsCount);
+    }
+
     delete[] name;
 }
 
+#define VALIDATE_PROGRAM_NAME(program) \
+    bool isShaderOrProgramObject = \
+        ctx->m_shared->isShaderOrProgramObject(program); \
+    bool isProgram = \
+        ctx->m_shared->isProgram(program); \
+    SET_ERROR_IF(!isShaderOrProgramObject, GL_INVALID_VALUE); \
+    SET_ERROR_IF(!isProgram, GL_INVALID_OPERATION); \
+
+#define VALIDATE_PROGRAM_NAME_RET(program, ret) \
+    bool isShaderOrProgramObject = \
+        ctx->m_shared->isShaderOrProgramObject(program); \
+    bool isProgram = \
+        ctx->m_shared->isProgram(program); \
+    RET_AND_SET_ERROR_IF(!isShaderOrProgramObject, GL_INVALID_VALUE, ret); \
+    RET_AND_SET_ERROR_IF(!isProgram, GL_INVALID_OPERATION, ret); \
+
+#define VALIDATE_SHADER_NAME(shader) \
+    bool isShaderOrProgramObject = \
+        ctx->m_shared->isShaderOrProgramObject(shader); \
+    bool isShader = \
+        ctx->m_shared->isShader(shader); \
+    SET_ERROR_IF(!isShaderOrProgramObject, GL_INVALID_VALUE); \
+    SET_ERROR_IF(!isShader, GL_INVALID_OPERATION); \
+
 void GL2Encoder::s_glDeleteProgram(void *self, GLuint program)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
+
+    VALIDATE_PROGRAM_NAME(program);
+
     ctx->m_glDeleteProgram_enc(self, program);
 
     ctx->m_shared->deleteProgramData(program);
@@ -1850,9 +1977,9 @@
     SET_ERROR_IF(!ctx->m_shared->isShaderOrProgramObject(program), GL_INVALID_VALUE);
     SET_ERROR_IF(!ctx->m_shared->isProgram(program), GL_INVALID_OPERATION);
     SET_ERROR_IF(!ctx->m_shared->isProgramInitialized(program), GL_INVALID_OPERATION);
-    GLint hostLoc = location;
-    SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,hostLoc)==0, GL_INVALID_OPERATION);
-    ctx->m_glGetUniformiv_enc(self, program, hostLoc, params);
+    SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,location)==0, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!ctx->m_shared->isProgramUniformLocationValid(program,location), GL_INVALID_OPERATION);
+    ctx->m_glGetUniformiv_enc(self, program, location, params);
 }
 void GL2Encoder::s_glGetUniformfv(void *self, GLuint program, GLint location, GLfloat* params)
 {
@@ -1860,9 +1987,9 @@
     SET_ERROR_IF(!ctx->m_shared->isShaderOrProgramObject(program), GL_INVALID_VALUE);
     SET_ERROR_IF(!ctx->m_shared->isProgram(program), GL_INVALID_OPERATION);
     SET_ERROR_IF(!ctx->m_shared->isProgramInitialized(program), GL_INVALID_OPERATION);
-    GLint hostLoc = location;
-    SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,hostLoc)==0, GL_INVALID_OPERATION);
-    ctx->m_glGetUniformfv_enc(self, program, hostLoc, params);
+    SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,location)==0, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!ctx->m_shared->isProgramUniformLocationValid(program,location), GL_INVALID_OPERATION);
+    ctx->m_glGetUniformfv_enc(self, program, location, params);
 }
 
 GLuint GL2Encoder::s_glCreateProgram(void * self)
@@ -1880,7 +2007,7 @@
     RET_AND_SET_ERROR_IF(!GLESv2Validation::shaderType(ctx, shaderType), GL_INVALID_ENUM, 0);
     GLuint shader = ctx->m_glCreateShader_enc(self, shaderType);
     if (shader != 0) {
-        if (!ctx->m_shared->addShaderData(shader)) {
+        if (!ctx->m_shared->addShaderData(shader, shaderType)) {
             ctx->m_glDeleteShader_enc(self, shader);
             return 0;
         }
@@ -1892,6 +2019,7 @@
         GLsizei* count, GLuint* shaders)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
+    VALIDATE_PROGRAM_NAME(program);
     SET_ERROR_IF(maxCount < 0, GL_INVALID_VALUE);
     ctx->m_glGetAttachedShaders_enc(self, program, maxCount, count, shaders);
 }
@@ -1900,6 +2028,7 @@
             GLsizei* length, GLchar* source)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
+    VALIDATE_SHADER_NAME(shader);
     SET_ERROR_IF(bufsize < 0, GL_INVALID_VALUE);
     ctx->m_glGetShaderSource_enc(self, shader, bufsize, length, source);
     ShaderData* shaderData = ctx->m_shared->getShaderData(shader);
@@ -1925,6 +2054,7 @@
         GLsizei* length, GLchar* infolog)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
+    VALIDATE_SHADER_NAME(shader);
     SET_ERROR_IF(bufsize < 0, GL_INVALID_VALUE);
     ctx->m_glGetShaderInfoLog_enc(self, shader, bufsize, length, infolog);
 }
@@ -1933,6 +2063,7 @@
         GLsizei* length, GLchar* infolog)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
+    VALIDATE_PROGRAM_NAME(program);
     SET_ERROR_IF(bufsize < 0, GL_INVALID_VALUE);
     ctx->m_glGetProgramInfoLog_enc(self, program, bufsize, length, infolog);
 }
@@ -1940,6 +2071,15 @@
 void GL2Encoder::s_glDeleteShader(void *self, GLenum shader)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
+
+    bool isShaderOrProgramObject =
+        ctx->m_shared->isShaderOrProgramObject(shader);
+    bool isShader =
+        ctx->m_shared->isShader(shader);
+
+    SET_ERROR_IF(isShaderOrProgramObject && !isShader, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!isShaderOrProgramObject && !isShader, GL_INVALID_VALUE);
+
     ctx->m_glDeleteShader_enc(self,shader);
     ctx->m_shared->unrefShaderData(shader);
 }
@@ -1947,15 +2087,36 @@
 void GL2Encoder::s_glAttachShader(void *self, GLuint program, GLuint shader)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
+    bool programIsShaderOrProgram = ctx->m_shared->isShaderOrProgramObject(program);
+    bool programIsProgram = ctx->m_shared->isProgram(program);
+    bool shaderIsShaderOrProgram = ctx->m_shared->isShaderOrProgramObject(shader);
+    bool shaderIsShader = ctx->m_shared->isShader(shader);
+
+    SET_ERROR_IF(!programIsShaderOrProgram, GL_INVALID_VALUE);
+    SET_ERROR_IF(!shaderIsShaderOrProgram, GL_INVALID_VALUE);
+    SET_ERROR_IF(!programIsProgram, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!shaderIsShader, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!ctx->m_shared->attachShader(program, shader), GL_INVALID_OPERATION);
+
     ctx->m_glAttachShader_enc(self, program, shader);
-    ctx->m_shared->attachShader(program, shader);
 }
 
 void GL2Encoder::s_glDetachShader(void *self, GLuint program, GLuint shader)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
+
+    bool programIsShaderOrProgram = ctx->m_shared->isShaderOrProgramObject(program);
+    bool programIsProgram = ctx->m_shared->isProgram(program);
+    bool shaderIsShaderOrProgram = ctx->m_shared->isShaderOrProgramObject(shader);
+    bool shaderIsShader = ctx->m_shared->isShader(shader);
+
+    SET_ERROR_IF(!programIsShaderOrProgram, GL_INVALID_VALUE);
+    SET_ERROR_IF(!shaderIsShaderOrProgram, GL_INVALID_VALUE);
+    SET_ERROR_IF(!programIsProgram, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!shaderIsShader, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!ctx->m_shared->detachShader(program, shader), GL_INVALID_OPERATION);
+
     ctx->m_glDetachShader_enc(self, program, shader);
-    ctx->m_shared->detachShader(program, shader);
 }
 
 int sArrIndexOfUniformExpr(const char* name, int* err) {
@@ -1975,6 +2136,16 @@
 {
     if (!name) return -1;
     GL2Encoder *ctx = (GL2Encoder*)self;
+
+    bool isShaderOrProgramObject =
+        ctx->m_shared->isShaderOrProgramObject(program);
+    bool isProgram =
+        ctx->m_shared->isProgram(program);
+
+    RET_AND_SET_ERROR_IF(!isShaderOrProgramObject, GL_INVALID_VALUE, -1);
+    RET_AND_SET_ERROR_IF(!isProgram, GL_INVALID_OPERATION, -1);
+    RET_AND_SET_ERROR_IF(!ctx->m_shared->getProgramLinkStatus(program), GL_INVALID_OPERATION, -1);
+
     return ctx->m_glGetUniformLocation_enc(self, program, name);
 }
 
@@ -2035,26 +2206,34 @@
 
     SET_ERROR_IF(program && !shared->isShaderOrProgramObject(program), GL_INVALID_VALUE);
     SET_ERROR_IF(program && !shared->isProgram(program), GL_INVALID_OPERATION);
+    SET_ERROR_IF(ctx->m_state->getTransformFeedbackActiveUnpaused(), GL_INVALID_OPERATION);
 
     ctx->m_glUseProgram_enc(self, program);
+
+    GLuint currProgram = ctx->m_state->currentProgram();
+    ctx->m_shared->onUseProgram(currProgram, program);
+
     ctx->m_state->setCurrentProgram(program);
     ctx->m_state->setCurrentShaderProgram(program);
-
     ctx->updateHostTexture2DBindingsFromProgramData(program);
+
+    if (program) {
+        ctx->m_state->currentUniformValidationInfo = ctx->m_shared->getUniformValidationInfo(program);
+    }
 }
 
 void GL2Encoder::s_glUniform1f(void *self , GLint location, GLfloat x)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform1f_enc(self, hostLoc, x);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 1 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform1f_enc(self, location, x);
 }
 
 void GL2Encoder::s_glUniform1fv(void *self , GLint location, GLsizei count, const GLfloat* v)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform1fv_enc(self, hostLoc, count, v);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 1 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform1fv_enc(self, location, count, v);
 }
 
 void GL2Encoder::s_glUniform1i(void *self , GLint location, GLint x)
@@ -2063,8 +2242,9 @@
     GLClientState* state = ctx->m_state;
     GLSharedGroupPtr shared = ctx->m_shared;
 
-    GLint hostLoc = location;
-    ctx->m_glUniform1i_enc(self, hostLoc, x);
+    ctx->m_state->validateUniform(false /* is float? */, false /* is unsigned? */, 1 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+
+    ctx->m_glUniform1i_enc(self, location, x);
 
     GLenum target;
     if (shared->setSamplerUniform(state->currentShaderProgram(), location, x, &target)) {
@@ -2079,113 +2259,113 @@
 void GL2Encoder::s_glUniform1iv(void *self , GLint location, GLsizei count, const GLint* v)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform1iv_enc(self, hostLoc, count, v);
+    ctx->m_state->validateUniform(false /* is float? */, false /* is unsigned? */, 1 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform1iv_enc(self, location, count, v);
 }
 
 void GL2Encoder::s_glUniform2f(void *self , GLint location, GLfloat x, GLfloat y)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform2f_enc(self, hostLoc, x, y);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 2 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform2f_enc(self, location, x, y);
 }
 
 void GL2Encoder::s_glUniform2fv(void *self , GLint location, GLsizei count, const GLfloat* v)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform2fv_enc(self, hostLoc, count, v);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 2 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform2fv_enc(self, location, count, v);
 }
 
 void GL2Encoder::s_glUniform2i(void *self , GLint location, GLint x, GLint y)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform2i_enc(self, hostLoc, x, y);
+    ctx->m_state->validateUniform(false /* is float? */, false /* is unsigned? */, 2 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform2i_enc(self, location, x, y);
 }
 
 void GL2Encoder::s_glUniform2iv(void *self , GLint location, GLsizei count, const GLint* v)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform2iv_enc(self, hostLoc, count, v);
+    ctx->m_state->validateUniform(false /* is float? */, false /* is unsigned? */, 2 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform2iv_enc(self, location, count, v);
 }
 
 void GL2Encoder::s_glUniform3f(void *self , GLint location, GLfloat x, GLfloat y, GLfloat z)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform3f_enc(self, hostLoc, x, y, z);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 3 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform3f_enc(self, location, x, y, z);
 }
 
 void GL2Encoder::s_glUniform3fv(void *self , GLint location, GLsizei count, const GLfloat* v)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform3fv_enc(self, hostLoc, count, v);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 3 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform3fv_enc(self, location, count, v);
 }
 
 void GL2Encoder::s_glUniform3i(void *self , GLint location, GLint x, GLint y, GLint z)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform3i_enc(self, hostLoc, x, y, z);
+    ctx->m_state->validateUniform(false /* is float? */, false /* is unsigned? */, 3 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform3i_enc(self, location, x, y, z);
 }
 
 void GL2Encoder::s_glUniform3iv(void *self , GLint location, GLsizei count, const GLint* v)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform3iv_enc(self, hostLoc, count, v);
+    ctx->m_state->validateUniform(false /* is float? */, false /* is unsigned? */, 3 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform3iv_enc(self, location, count, v);
 }
 
 void GL2Encoder::s_glUniform4f(void *self , GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform4f_enc(self, hostLoc, x, y, z, w);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 4 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform4f_enc(self, location, x, y, z, w);
 }
 
 void GL2Encoder::s_glUniform4fv(void *self , GLint location, GLsizei count, const GLfloat* v)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform4fv_enc(self, hostLoc, count, v);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 4 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform4fv_enc(self, location, count, v);
 }
 
 void GL2Encoder::s_glUniform4i(void *self , GLint location, GLint x, GLint y, GLint z, GLint w)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform4i_enc(self, hostLoc, x, y, z, w);
+    ctx->m_state->validateUniform(false /* is float? */, false /* is unsigned? */, 4 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform4i_enc(self, location, x, y, z, w);
 }
 
 void GL2Encoder::s_glUniform4iv(void *self , GLint location, GLsizei count, const GLint* v)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform4iv_enc(self, hostLoc, count, v);
+    ctx->m_state->validateUniform(false /* is float? */, false /* is unsigned? */, 4 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform4iv_enc(self, location, count, v);
 }
 
 void GL2Encoder::s_glUniformMatrix2fv(void *self , GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniformMatrix2fv_enc(self, hostLoc, count, transpose, value);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 2 /* columns */, 2 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniformMatrix2fv_enc(self, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glUniformMatrix3fv(void *self , GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniformMatrix3fv_enc(self, hostLoc, count, transpose, value);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 3 /* columns */, 3 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniformMatrix3fv_enc(self, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glUniformMatrix4fv(void *self , GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniformMatrix4fv_enc(self, hostLoc, count, transpose, value);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 4 /* columns */, 4 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniformMatrix4fv_enc(self, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glActiveTexture(void* self, GLenum texture)
@@ -2194,6 +2374,10 @@
     GLClientState* state = ctx->m_state;
     GLenum err;
 
+    GLint maxCombinedUnits;
+    ctx->glGetIntegerv(ctx, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxCombinedUnits);
+
+    SET_ERROR_IF(texture - GL_TEXTURE0 > maxCombinedUnits - 1, GL_INVALID_ENUM);
     SET_ERROR_IF((err = state->setActiveTextureUnit(texture)) != GL_NO_ERROR, err);
 
     ctx->m_glActiveTexture_enc(ctx, texture);
@@ -2250,6 +2434,10 @@
 {
     GL2Encoder* ctx = (GL2Encoder*)self;
 
+    SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParams(ctx, pname), GL_INVALID_ENUM);
+    if (!params) return;
+
     if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
         ctx->override2DTextureTarget(target);
         ctx->m_glGetTexParameterfv_enc(ctx, GL_TEXTURE_2D, pname, params);
@@ -2264,6 +2452,11 @@
 {
     GL2Encoder* ctx = (GL2Encoder*)self;
 
+    SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParams(ctx, pname), GL_INVALID_ENUM);
+
+    if (!params) return;
+
     switch (pname) {
     case GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES:
         *params = 1;
@@ -2305,6 +2498,9 @@
     SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES &&
             !isValidTextureExternalParam(pname, (GLenum)param)),
             GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParams(ctx, pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParamValue(ctx, pname, (GLint)param, param, (GLenum)param), GL_INVALID_ENUM);
 
     if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
         ctx->override2DTextureTarget(target);
@@ -2323,6 +2519,11 @@
     SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES &&
             !isValidTextureExternalParam(pname, (GLenum)params[0])),
             GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParams(ctx, pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(!params, GL_INVALID_VALUE);
+    GLfloat param = *params;
+    SET_ERROR_IF(!GLESv2Validation::textureParamValue(ctx, pname, (GLint)param, param, (GLenum)param), GL_INVALID_ENUM);
 
     if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
         ctx->override2DTextureTarget(target);
@@ -2341,6 +2542,9 @@
     SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES &&
             !isValidTextureExternalParam(pname, (GLenum)param)),
             GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParams(ctx, pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParamValue(ctx, pname, param, (GLfloat)param, (GLenum)param), GL_INVALID_ENUM);
 
     if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
         ctx->override2DTextureTarget(target);
@@ -2368,6 +2572,8 @@
     SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::pixelType(ctx, type), GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::pixelFormat(ctx, format), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::pixelFormat(ctx, internalformat) && !GLESv2Validation::pixelInternalFormat(internalformat), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::pixelSizedFormat(ctx, internalformat, format, type), GL_INVALID_OPERATION);
     // If unpack buffer is nonzero, verify unmapped state.
     SET_ERROR_IF(ctx->isBufferTargetMapped(GL_PIXEL_UNPACK_BUFFER), GL_INVALID_OPERATION);
 
@@ -2384,6 +2590,7 @@
     SET_ERROR_IF(height > max_texture_size, GL_INVALID_VALUE);
     SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && width > max_cube_map_texture_size, GL_INVALID_VALUE);
     SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && height > max_cube_map_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && (width != height), GL_INVALID_VALUE);
     SET_ERROR_IF(border != 0, GL_INVALID_VALUE);
     // If unpack buffer is nonzero, verify buffer data fits and is evenly divisible by the type.
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
@@ -2414,7 +2621,8 @@
     state->setBoundTextureInternalFormat(stateTarget, internalformat);
     state->setBoundTextureFormat(stateTarget, format);
     state->setBoundTextureType(stateTarget, type);
-    state->setBoundTextureDims(stateTarget, level, width, height, 1);
+    state->setBoundTextureDims(stateTarget, target, level, width, height, 1);
+    state->addTextureCubeMapImage(stateTarget, target);
 
     if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
         ctx->override2DTextureTarget(target);
@@ -2475,14 +2683,15 @@
     }
 
     // If unpack buffer is nonzero, verify buffer data fits and is evenly divisible by the type.
+
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
                  ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
-                 (state->pboNeededDataSize(width, height, 1, format, type, 0) >
+                 (state->pboNeededDataSize(width, height, 1, format, type, 0) + (uintptr_t)pixels >
                   ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size),
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
                  ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
-                 (ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size %
+                 ((uintptr_t)pixels %
                   glSizeof(type)),
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(!ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) && !pixels, GL_INVALID_OPERATION);
@@ -2513,6 +2722,35 @@
     GL2Encoder* ctx = (GL2Encoder*)self;
     GLClientState* state = ctx->m_state;
 
+    SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::pixelFormat(ctx, internalformat) && !GLESv2Validation::pixelInternalFormat(internalformat), GL_INVALID_ENUM);
+    GLint max_texture_size;
+    GLint max_cube_map_texture_size;
+    ctx->glGetIntegerv(ctx, GL_MAX_TEXTURE_SIZE, &max_texture_size);
+    ctx->glGetIntegerv(ctx, GL_MAX_CUBE_MAP_TEXTURE_SIZE, &max_cube_map_texture_size);
+    SET_ERROR_IF(level < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(level > ilog2(max_texture_size), GL_INVALID_VALUE);
+    SET_ERROR_IF((target == GL_TEXTURE_CUBE_MAP) &&
+                 (level > ilog2(max_cube_map_texture_size)), GL_INVALID_VALUE);
+    SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(width > max_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(height > max_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && width > max_cube_map_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && height > max_cube_map_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && (width != height), GL_INVALID_VALUE);
+    SET_ERROR_IF(border != 0, GL_INVALID_VALUE);
+
+    GLenum stateTarget = target;
+    if (target == GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
+        target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y ||
+        target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
+        stateTarget = GL_TEXTURE_CUBE_MAP;
+
+    SET_ERROR_IF(state->isBoundTextureImmutableFormat(target), GL_INVALID_OPERATION);
+
     SET_ERROR_IF(ctx->glCheckFramebufferStatus(ctx, GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
                  GL_INVALID_FRAMEBUFFER_OPERATION);
     // This is needed to work around underlying OpenGL drivers
@@ -2523,6 +2761,10 @@
         state->copyTexImageLuminanceCubeMapAMDWorkaround
             (target, level, internalformat);
 
+    state->setBoundTextureInternalFormat(stateTarget, internalformat);
+    state->setBoundTextureDims(stateTarget, target, level, width, height, 1);
+    state->addTextureCubeMapImage(stateTarget, target);
+
     if (extraTarget) {
         ctx->m_glCopyTexImage2D_enc(ctx, extraTarget, level, internalformat,
                                     x, y, width, height, border);
@@ -2530,9 +2772,6 @@
 
     ctx->m_glCopyTexImage2D_enc(ctx, target, level, internalformat,
                                 x, y, width, height, border);
-
-    state->setBoundTextureInternalFormat(target, internalformat);
-    state->setBoundTextureDims(target, level, width, height, 1);
 }
 
 void GL2Encoder::s_glTexParameteriv(void* self,
@@ -2543,6 +2782,11 @@
     SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES &&
             !isValidTextureExternalParam(pname, (GLenum)params[0])),
             GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParams(ctx, pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(!params, GL_INVALID_VALUE);
+    GLint param = *params;
+    SET_ERROR_IF(!GLESv2Validation::textureParamValue(ctx, pname, param, (GLfloat)param, (GLenum)param), GL_INVALID_ENUM);
 
     if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
         ctx->override2DTextureTarget(target);
@@ -2643,7 +2887,7 @@
     for (int i = 0; i < n; i++) {
         state->detachRbo(renderbuffers[i]);
     }
-    // state->removeRenderbuffers(n, renderbuffers);
+    state->removeRenderbuffers(n, renderbuffers);
 }
 
 void GL2Encoder::s_glBindRenderbuffer(void* self,
@@ -2669,8 +2913,14 @@
         !GLESv2Validation::rboFormat(ctx, internalformat),
         GL_INVALID_ENUM);
 
+    SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
+    GLint max_rb_size;
+    ctx->glGetIntegerv(ctx, GL_MAX_RENDERBUFFER_SIZE, &max_rb_size);
+    SET_ERROR_IF(width > max_rb_size || height > max_rb_size, GL_INVALID_VALUE);
+
     state->setBoundRenderbufferFormat(internalformat);
     state->setBoundRenderbufferSamples(0);
+    state->setBoundRenderbufferDimensions(width, height);
 
     ctx->m_glRenderbufferStorage_enc(self, target, internalformat,
                                      width, height);
@@ -2684,6 +2934,10 @@
 
     SET_ERROR_IF(!GLESv2Validation::framebufferTarget(ctx, target), GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::framebufferAttachment(ctx, attachment), GL_INVALID_ENUM);
+    SET_ERROR_IF(GL_RENDERBUFFER != renderbuffertarget, GL_INVALID_ENUM);
+    SET_ERROR_IF(!state->getBoundFramebuffer(target), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!state->isRenderbufferThatWasBound(renderbuffer), GL_INVALID_OPERATION);
+
     state->attachRbo(target, attachment, renderbuffer);
 
     ctx->m_glFramebufferRenderbuffer_enc(self, target, attachment, renderbuffertarget, renderbuffer);
@@ -2730,8 +2984,21 @@
     GLClientState* state = ctx->m_state;
 
     SET_ERROR_IF(!GLESv2Validation::framebufferTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, textarget), GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::framebufferAttachment(ctx, attachment), GL_INVALID_ENUM);
-    state->attachTextureObject(target, attachment, texture);
+    SET_ERROR_IF(!state->getBoundFramebuffer(target), GL_INVALID_OPERATION);
+    SET_ERROR_IF(texture && !state->isTexture(texture), GL_INVALID_OPERATION);
+    SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(textarget) && !state->isTextureCubeMap(texture), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!GLESv2Validation::isCubeMapTarget(textarget) && state->isTextureCubeMap(texture), GL_INVALID_OPERATION);
+    SET_ERROR_IF((texture && (level < 0)), GL_INVALID_VALUE);
+
+    if (target == GL_TEXTURE_2D) {
+        SET_ERROR_IF(level > ilog2(ctx->m_state->getMaxTextureSize()), GL_INVALID_VALUE);
+    } else {
+        SET_ERROR_IF(level > ilog2(ctx->m_state->getMaxTextureSizeCubeMap()), GL_INVALID_VALUE);
+    }
+
+    state->attachTextureObject(target, attachment, texture, level, 0);
 
     ctx->m_glFramebufferTexture2D_enc(self, target, attachment, textarget, texture, level);
 }
@@ -2742,7 +3009,7 @@
     GL2Encoder* ctx = (GL2Encoder*)self;
     GLClientState* state = ctx->m_state;
 
-    state->attachTextureObject(target, attachment, texture);
+    state->attachTextureObject(target, attachment, texture, level, zoffset);
 
     ctx->m_glFramebufferTexture3DOES_enc(self, target, attachment, textarget, texture, level, zoffset);
 }
@@ -2752,6 +3019,12 @@
     GL2Encoder* ctx = (GL2Encoder*)self;
     const GLClientState* state = ctx->m_state;
     SET_ERROR_IF(!GLESv2Validation::framebufferTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!state->boundFramebuffer(target) &&
+                 attachment != GL_BACK &&
+                 attachment != GL_FRONT &&
+                 attachment != GL_DEPTH &&
+                 attachment != GL_STENCIL,
+                 GL_INVALID_OPERATION);
     SET_ERROR_IF(pname != GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME &&
                  pname != GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE &&
                  !state->attachmentHasObject(target, attachment),
@@ -2764,6 +3037,11 @@
                   FBO_ATTACHMENT_TEXTURE),
                  !state->attachmentHasObject(target, attachment) ?
                  GL_INVALID_OPERATION : GL_INVALID_ENUM);
+    SET_ERROR_IF(
+        (attachment == GL_FRONT ||
+         attachment == GL_BACK) &&
+        (pname == GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME),
+        GL_INVALID_ENUM);
     SET_ERROR_IF(attachment == GL_DEPTH_STENCIL_ATTACHMENT &&
                  pname == GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME &&
                  (state->objectOfAttachment(target, GL_DEPTH_ATTACHMENT) !=
@@ -2771,129 +3049,23 @@
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(state->boundFramebuffer(target) &&
                  (attachment == GL_BACK ||
-                  attachment == GL_FRONT),
+                  attachment == GL_FRONT ||
+                  attachment == GL_DEPTH || 
+                  attachment == GL_STENCIL),
                  GL_INVALID_OPERATION);
     ctx->m_glGetFramebufferAttachmentParameteriv_enc(self, target, attachment, pname, params);
 }
 
-bool GL2Encoder::isCompleteFbo(GLenum target, const GLClientState* state,
-                               GLenum attachment) const {
-    FboFormatInfo fbo_format_info;
-    state->getBoundFramebufferFormat(target, attachment, &fbo_format_info);
-
-    bool res;
-    switch (fbo_format_info.type) {
-    case FBO_ATTACHMENT_RENDERBUFFER:
-        switch (fbo_format_info.rb_format) {
-        case GL_R16F:
-        case GL_RG16F:
-        case GL_RGBA16F:
-        case GL_R32F:
-        case GL_RG32F:
-        case GL_RGBA32F:
-        case GL_R11F_G11F_B10F:
-            res = majorVersion() >= 3 && hasExtension("GL_EXT_color_buffer_float");
-            break;
-        case GL_RGB16F:
-            res = majorVersion() >= 3 && hasExtension("GL_EXT_color_buffer_half_float");
-            break;
-        case GL_STENCIL_INDEX8:
-            if (attachment == GL_STENCIL_ATTACHMENT) {
-                res = true;
-            } else {
-                res = false;
-            }
-            break;
-        default:
-            res = true;
-        }
-        break;
-    case FBO_ATTACHMENT_TEXTURE:
-        switch (fbo_format_info.tex_internalformat) {
-        case GL_R16F:
-        case GL_RG16F:
-        case GL_RGBA16F:
-        case GL_R32F:
-        case GL_RG32F:
-        case GL_RGBA32F:
-        case GL_R11F_G11F_B10F:
-            res = majorVersion() >= 3 && hasExtension("GL_EXT_color_buffer_float");
-            break;
-        case GL_RGB16F:
-            res = majorVersion() >= 3 && hasExtension("GL_EXT_color_buffer_half_float");
-            break;
-        case GL_RED:
-        case GL_RG:
-        case GL_SRGB8:
-        case GL_RGB32UI:
-        case GL_RGB16UI:
-        case GL_RGB8UI:
-        case GL_RGB32I:
-        case GL_RGB16I:
-        case GL_RGB8I:
-        case GL_R8_SNORM:
-        case GL_RG8_SNORM:
-        case GL_RGB8_SNORM:
-        case GL_RGBA8_SNORM:
-            res = false;
-            break;
-        // No float/half-float formats allowed for RGB(A)
-        case GL_RGB:
-        case GL_RGBA:
-            switch (fbo_format_info.tex_type) {
-            case GL_FLOAT:
-            case GL_HALF_FLOAT_OES:
-            case GL_UNSIGNED_INT_10F_11F_11F_REV:
-            case GL_UNSIGNED_INT_2_10_10_10_REV:
-                res = false;
-                break;
-            default:
-                res = true;
-            }
-            break;
-        default:
-            res = true;
-        }
-        break;
-    case FBO_ATTACHMENT_NONE:
-        res = true;
-        break;
-    default:
-        res = true;
-    }
-    return res;
-}
-
-bool GL2Encoder::checkFramebufferCompleteness(GLenum target, const GLClientState* state) const {
-    bool res = true;
-
-    for (int i = 0; i < state->getMaxColorAttachments(); i++) {
-        res = res && isCompleteFbo(target, state, glUtilsColorAttachmentName(i));
-    }
-
-    res = res && isCompleteFbo(target, state, GL_DEPTH_ATTACHMENT);
-    res = res && isCompleteFbo(target, state, GL_STENCIL_ATTACHMENT);
-
-    return res;
-}
-
 GLenum GL2Encoder::s_glCheckFramebufferStatus(void* self, GLenum target) {
     GL2Encoder* ctx = (GL2Encoder*)self;
+
+    RET_AND_SET_ERROR_IF(
+        target != GL_DRAW_FRAMEBUFFER && target != GL_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER,
+        GL_INVALID_ENUM, 0);
+
     GLClientState* state = ctx->m_state;
 
-    bool fboCompleteByCodec =
-        ctx->checkFramebufferCompleteness(target, state);
-
-    if (!fboCompleteByCodec) {
-        state->setCheckFramebufferStatus(target, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
-        return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
-    } else {
-        // double check with underlying opengl to avoid craziness.
-        GLenum host_checkstatus = ctx->m_glCheckFramebufferStatus_enc(self, target);
-        state->setCheckFramebufferStatus(target, host_checkstatus);
-        if (host_checkstatus == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS) return GL_FRAMEBUFFER_COMPLETE;
-        return host_checkstatus;
-    }
+    return state->checkFramebufferCompleteness(target);
 }
 
 void GL2Encoder::s_glGenVertexArrays(void* self, GLsizei n, GLuint* arrays) {
@@ -3154,6 +3326,7 @@
     GLClientState* state = ctx->m_state;
 
     SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(target == GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM);
     // Filter compressed formats support.
     SET_ERROR_IF(!GLESv2Validation::supportedCompressedFormat(ctx, internalformat), GL_INVALID_ENUM);
     // Verify level <= log2(GL_MAX_TEXTURE_SIZE).
@@ -3170,14 +3343,13 @@
     // If unpack buffer is nonzero, verify unmapped state.
     SET_ERROR_IF(ctx->isBufferTargetMapped(GL_PIXEL_UNPACK_BUFFER), GL_INVALID_OPERATION);
     SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
+
     // If unpack buffer is nonzero, verify buffer data fits.
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
                  ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
                  (imageSize > ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size),
                  GL_INVALID_OPERATION);
-    // TODO: Fix:
-    // If |imageSize| is inconsistent with compressed dimensions.
-    // SET_ERROR_IF(GLESv2Validation::compressedTexImageSize(internalformat, width, height, 1) != imageSize, GL_INVALID_VALUE);
+    SET_ERROR_IF(!ctx->m_state->compressedTexImageSizeCompatible(internalformat, width, height, 1, imageSize), GL_INVALID_VALUE);
 
     GLenum stateTarget = target;
     if (target == GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
@@ -3188,7 +3360,7 @@
         target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
         stateTarget = GL_TEXTURE_CUBE_MAP;
     state->setBoundTextureInternalFormat(stateTarget, (GLint)internalformat);
-    state->setBoundTextureDims(stateTarget, level, width, height, 1);
+    state->setBoundTextureDims(stateTarget, target, level, width, height, 1);
 
     if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
         ctx->override2DTextureTarget(target);
@@ -3215,13 +3387,28 @@
     GL2Encoder* ctx = (GL2Encoder*)self;
 
     SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(target == GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM);
     // If unpack buffer is nonzero, verify unmapped state.
     SET_ERROR_IF(ctx->isBufferTargetMapped(GL_PIXEL_UNPACK_BUFFER), GL_INVALID_OPERATION);
+
+    GLenum stateTarget = target;
+    if (target == GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
+        target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y ||
+        target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
+        stateTarget = GL_TEXTURE_CUBE_MAP;
+    GLuint tex = ctx->m_state->getBoundTexture(stateTarget);
+
+    GLint internalFormat = ctx->m_state->queryTexInternalFormat(tex);
+    SET_ERROR_IF(internalFormat != format, GL_INVALID_OPERATION);
+    SET_ERROR_IF(level < 0, GL_INVALID_VALUE);
+
     GLint max_texture_size;
     GLint max_cube_map_texture_size;
     ctx->glGetIntegerv(ctx, GL_MAX_TEXTURE_SIZE, &max_texture_size);
     ctx->glGetIntegerv(ctx, GL_MAX_CUBE_MAP_TEXTURE_SIZE, &max_cube_map_texture_size);
-    SET_ERROR_IF(level < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(level > ilog2(max_texture_size), GL_INVALID_VALUE);
     SET_ERROR_IF(level > ilog2(max_cube_map_texture_size), GL_INVALID_VALUE);
     SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
@@ -3232,6 +3419,20 @@
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(xoffset < 0 || yoffset < 0, GL_INVALID_VALUE);
 
+    GLint totalWidth = ctx->m_state->queryTexWidth(level, tex);
+    GLint totalHeight = ctx->m_state->queryTexHeight(level, tex);
+
+    if (GLESTextureUtils::isEtc2Format(internalFormat)) {
+        SET_ERROR_IF((width % 4) && (totalWidth != xoffset + width), GL_INVALID_OPERATION);
+        SET_ERROR_IF((height % 4) && (totalHeight != yoffset + height), GL_INVALID_OPERATION);
+        SET_ERROR_IF((xoffset % 4) || (yoffset % 4), GL_INVALID_OPERATION);
+    }
+
+    SET_ERROR_IF(totalWidth < xoffset + width, GL_INVALID_VALUE);
+    SET_ERROR_IF(totalHeight < yoffset + height, GL_INVALID_VALUE);
+
+    SET_ERROR_IF(!ctx->m_state->compressedTexImageSizeCompatible(internalFormat, width, height, 1, imageSize), GL_INVALID_VALUE);
+
     if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
         ctx->override2DTextureTarget(target);
     }
@@ -3360,22 +3561,33 @@
                   writetarget == GL_DISPATCH_INDIRECT_BUFFER ||
                   writetarget == GL_DRAW_INDIRECT_BUFFER ||
                   writetarget == GL_SHADER_STORAGE_BUFFER), GL_INVALID_ENUM);
-    SET_ERROR_IF(!ctx->boundBuffer(readtarget), GL_INVALID_OPERATION);
-    SET_ERROR_IF(!ctx->boundBuffer(writetarget), GL_INVALID_OPERATION);
+
+    GLuint readBufferId = ctx->boundBuffer(readtarget);
+    GLuint writeBufferId = ctx->boundBuffer(writetarget);
+
+    SET_ERROR_IF(!readBufferId || !writeBufferId, GL_INVALID_OPERATION);
+
     SET_ERROR_IF(ctx->isBufferTargetMapped(readtarget), GL_INVALID_OPERATION);
     SET_ERROR_IF(ctx->isBufferTargetMapped(writetarget), GL_INVALID_OPERATION);
+
     SET_ERROR_IF(readoffset < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(writeoffset < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(size < 0, GL_INVALID_VALUE);
+
+    BufferData* readBufferData = ctx->getBufferData(readtarget);
+    BufferData* writeBufferData = ctx->getBufferData(writetarget);
+
     SET_ERROR_IF(
-        ctx->getBufferData(readtarget) &&
-        (readoffset + size > ctx->getBufferData(readtarget)->m_size),
+        readBufferData &&
+        (readoffset + size > readBufferData->m_size),
         GL_INVALID_VALUE);
+
     SET_ERROR_IF(
-        ctx->getBufferData(writetarget) &&
-        (writeoffset + size > ctx->getBufferData(writetarget)->m_size),
+        writeBufferData &&
+        (writeoffset + size > writeBufferData->m_size),
         GL_INVALID_VALUE);
-    SET_ERROR_IF(readtarget == writetarget &&
+
+    SET_ERROR_IF(readBufferId == writeBufferId &&
                  !((writeoffset >= readoffset + size) ||
                    (readoffset >= writeoffset + size)),
                  GL_INVALID_VALUE);
@@ -3537,6 +3749,8 @@
 void GL2Encoder::s_glGetUniformIndices(void* self, GLuint program, GLsizei uniformCount, const GLchar ** uniformNames, GLuint* uniformIndices) {
     GL2Encoder* ctx = (GL2Encoder*)self;
 
+    VALIDATE_PROGRAM_NAME(program);
+
     if (!uniformCount) return;
 
     GLint err = GL_NO_ERROR;
@@ -3561,8 +3775,8 @@
     GLClientState* state = ctx->m_state;
     GLSharedGroupPtr shared = ctx->m_shared;
 
-    GLint hostLoc = location;
-    ctx->m_glUniform1ui_enc(self, hostLoc, v0);
+    ctx->m_state->validateUniform(false /* is float? */, true /* is unsigned? */, 1 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform1ui_enc(self, location, v0);
 
     GLenum target;
     if (shared->setSamplerUniform(state->currentShaderProgram(), location, v0, &target)) {
@@ -3576,80 +3790,80 @@
 
 void GL2Encoder::s_glUniform2ui(void* self, GLint location, GLuint v0, GLuint v1) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform2ui_enc(self, hostLoc, v0, v1);
+    ctx->m_state->validateUniform(false /* is float? */, true /* is unsigned? */, 2 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform2ui_enc(self, location, v0, v1);
 }
 
 void GL2Encoder::s_glUniform3ui(void* self, GLint location, GLuint v0, GLuint v1, GLuint v2) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform3ui_enc(self, hostLoc, v0, v1, v2);
+    ctx->m_state->validateUniform(false /* is float? */, true /* is unsigned? */, 3 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform3ui_enc(self, location, v0, v1, v2);
 }
 
 void GL2Encoder::s_glUniform4ui(void* self, GLint location, GLint v0, GLuint v1, GLuint v2, GLuint v3) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform4ui_enc(self, hostLoc, v0, v1, v2, v3);
+    ctx->m_state->validateUniform(false /* is float? */, true /* is unsigned? */, 4 /* columns */, 1 /* rows */, location, 1 /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform4ui_enc(self, location, v0, v1, v2, v3);
 }
 
 void GL2Encoder::s_glUniform1uiv(void* self, GLint location, GLsizei count, const GLuint *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform1uiv_enc(self, hostLoc, count, value);
+    ctx->m_state->validateUniform(false /* is float? */, true /* is unsigned? */, 1 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform1uiv_enc(self, location, count, value);
 }
 
 void GL2Encoder::s_glUniform2uiv(void* self, GLint location, GLsizei count, const GLuint *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform2uiv_enc(self, hostLoc, count, value);
+    ctx->m_state->validateUniform(false /* is float? */, true /* is unsigned? */, 2 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform2uiv_enc(self, location, count, value);
 }
 
 void GL2Encoder::s_glUniform3uiv(void* self, GLint location, GLsizei count, const GLuint *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform3uiv_enc(self, hostLoc, count, value);
+    ctx->m_state->validateUniform(false /* is float? */, true /* is unsigned? */, 3 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform3uiv_enc(self, location, count, value);
 }
 
 void GL2Encoder::s_glUniform4uiv(void* self, GLint location, GLsizei count, const GLuint *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniform4uiv_enc(self, hostLoc, count, value);
+    ctx->m_state->validateUniform(false /* is float? */, true /* is unsigned? */, 4 /* columns */, 1 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniform4uiv_enc(self, location, count, value);
 }
 
 void GL2Encoder::s_glUniformMatrix2x3fv(void* self, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniformMatrix2x3fv_enc(self, hostLoc, count, transpose, value);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 2 /* columns */, 3 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniformMatrix2x3fv_enc(self, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glUniformMatrix3x2fv(void* self, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniformMatrix3x2fv_enc(self, hostLoc, count, transpose, value);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 3 /* columns */, 2 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniformMatrix3x2fv_enc(self, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glUniformMatrix2x4fv(void* self, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniformMatrix2x4fv_enc(self, hostLoc, count, transpose, value);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 2 /* columns */, 4 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniformMatrix2x4fv_enc(self, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glUniformMatrix4x2fv(void* self, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniformMatrix4x2fv_enc(self, hostLoc, count, transpose, value);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 4 /* columns */, 2 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniformMatrix4x2fv_enc(self, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glUniformMatrix3x4fv(void* self, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniformMatrix3x4fv_enc(self, hostLoc, count, transpose, value);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 3 /* columns */, 4 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniformMatrix3x4fv_enc(self, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glUniformMatrix4x3fv(void* self, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glUniformMatrix4x3fv_enc(self, hostLoc, count, transpose, value);
+    ctx->m_state->validateUniform(true /* is float? */, false /* is unsigned? */, 4 /* columns */, 3 /* rows */, location, count /* count */, ctx->getErrorPtr());
+    ctx->m_glUniformMatrix4x3fv_enc(self, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glGetUniformuiv(void* self, GLuint program, GLint location, GLuint* params) {
@@ -3657,14 +3871,18 @@
     SET_ERROR_IF(!ctx->m_shared->isShaderOrProgramObject(program), GL_INVALID_VALUE);
     SET_ERROR_IF(!ctx->m_shared->isProgram(program), GL_INVALID_OPERATION);
     SET_ERROR_IF(!ctx->m_shared->isProgramInitialized(program), GL_INVALID_OPERATION);
-    GLint hostLoc = location;
-    SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,hostLoc)==0, GL_INVALID_OPERATION);
-    ctx->m_glGetUniformuiv_enc(self, program, hostLoc, params);
+    SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,location)==0, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!ctx->m_shared->isProgramUniformLocationValid(program,location), GL_INVALID_OPERATION);
+    ctx->m_glGetUniformuiv_enc(self, program, location, params);
 }
 
 void GL2Encoder::s_glGetActiveUniformBlockiv(void* self, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params) {
     GL2Encoder* ctx = (GL2Encoder*)self;
 
+    VALIDATE_PROGRAM_NAME(program);
+    SET_ERROR_IF(!GLESv2Validation::allowedGetActiveUniformBlock(pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(uniformBlockIndex >= ctx->m_shared->getActiveUniformBlockCount(program), GL_INVALID_VALUE);
+
     // refresh client state's # active uniforms in this block
     if (pname == GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES) {
         // TODO if worth it: cache uniform count and other params,
@@ -3685,10 +3903,8 @@
 
 void GL2Encoder::s_glGetVertexAttribIiv(void* self, GLuint index, GLenum pname, GLint* params) {
     GL2Encoder *ctx = (GL2Encoder *)self;
-    assert(ctx->m_state);
-    GLint maxIndex;
-    ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
-    SET_ERROR_IF(!(index < maxIndex), GL_INVALID_VALUE);
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
+    SET_ERROR_IF(!GLESv2Validation::allowedGetVertexAttrib(pname), GL_INVALID_ENUM);
 
     if (!ctx->m_state->getVertexAttribParameter<GLint>(index, pname, params)) {
         ctx->m_glGetVertexAttribIiv_enc(self, index, pname, params);
@@ -3697,10 +3913,8 @@
 
 void GL2Encoder::s_glGetVertexAttribIuiv(void* self, GLuint index, GLenum pname, GLuint* params) {
     GL2Encoder *ctx = (GL2Encoder *)self;
-    assert(ctx->m_state);
-    GLint maxIndex;
-    ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
-    SET_ERROR_IF(!(index < maxIndex), GL_INVALID_VALUE);
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
+    SET_ERROR_IF(!GLESv2Validation::allowedGetVertexAttrib(pname), GL_INVALID_ENUM);
 
     if (!ctx->m_state->getVertexAttribParameter<GLuint>(index, pname, params)) {
         ctx->m_glGetVertexAttribIuiv_enc(self, index, pname, params);
@@ -3756,12 +3970,18 @@
     SET_ERROR_IF(target != GL_RENDERBUFFER, GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::rboFormat(ctx, internalformat), GL_INVALID_ENUM);
 
+    SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
+    GLint max_rb_size;
+    ctx->glGetIntegerv(ctx, GL_MAX_RENDERBUFFER_SIZE, &max_rb_size);
+    SET_ERROR_IF(width > max_rb_size || height > max_rb_size, GL_INVALID_VALUE);
+
     GLint max_samples;
     ctx->s_glGetInternalformativ(ctx, target, internalformat, GL_SAMPLES, 1, &max_samples);
     SET_ERROR_IF(samples > max_samples, GL_INVALID_OPERATION);
 
     state->setBoundRenderbufferFormat(internalformat);
     state->setBoundRenderbufferSamples(samples);
+    state->setBoundRenderbufferDimensions(width, height);
     ctx->m_glRenderbufferStorageMultisample_enc(
             self, target, samples, internalformat, width, height);
 }
@@ -3833,11 +4053,16 @@
 
     SET_ERROR_IF(!GLESv2Validation::framebufferTarget(ctx, target), GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::framebufferAttachment(ctx, attachment), GL_INVALID_ENUM);
+    SET_ERROR_IF(texture != 0 && layer < 0, GL_INVALID_VALUE);
+    GLint maxArrayTextureLayers;
+    ctx->glGetIntegerv(ctx, GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArrayTextureLayers);
+    SET_ERROR_IF(texture != 0 && layer > maxArrayTextureLayers - 1, GL_INVALID_VALUE);
+    SET_ERROR_IF(!ctx->m_state->boundFramebuffer(target), GL_INVALID_OPERATION);
     GLenum lastBoundTarget = state->queryTexLastBoundTarget(texture);
     SET_ERROR_IF(lastBoundTarget != GL_TEXTURE_2D_ARRAY &&
                  lastBoundTarget != GL_TEXTURE_3D,
                  GL_INVALID_OPERATION);
-    state->attachTextureObject(target, attachment, texture);
+    state->attachTextureObject(target, attachment, texture, level, layer);
 
     GLint max3DTextureSize;
     ctx->glGetIntegerv(ctx, GL_MAX_3D_TEXTURE_SIZE, &max3DTextureSize);
@@ -3864,7 +4089,7 @@
     SET_ERROR_IF(state->isBoundTextureImmutableFormat(target), GL_INVALID_OPERATION);
 
     state->setBoundTextureInternalFormat(target, internalformat);
-    state->setBoundTextureDims(target, -1, width, height, 1);
+    state->setBoundTextureDims(target, -1 /* set all cube dimensions */, -1, width, height, 1);
     state->setBoundTextureImmutableFormat(target);
 
     if (target == GL_TEXTURE_2D) {
@@ -3895,6 +4120,11 @@
         bufferMode != GL_SEPARATE_ATTRIBS,
         GL_INVALID_ENUM);
 
+    // NOTE: This only has an effect on the program that is being linked.
+    // The dEQP test in dEQP-GLES3.functional.negative_api doesn't know
+    // about this.
+    ctx->m_state->setTransformFeedbackVaryingsCountForLinking(count);
+
     if (!count) return;
 
     GLint err = GL_NO_ERROR;
@@ -3907,29 +4137,51 @@
 void GL2Encoder::s_glBeginTransformFeedback(void* self, GLenum primitiveMode) {
     GL2Encoder* ctx = (GL2Encoder*)self;
     GLClientState* state = ctx->m_state;
+    SET_ERROR_IF(
+        primitiveMode != GL_POINTS &&
+        primitiveMode != GL_LINES &&
+        primitiveMode != GL_TRIANGLES,
+        GL_INVALID_ENUM);
+    SET_ERROR_IF(
+        ctx->m_state->getTransformFeedbackActive(),
+        GL_INVALID_OPERATION);
+    // TODO:
+    // dEQP-GLES3.functional.lifetime.attach.deleted_output.buffer_transform_feedback
+    // SET_ERROR_IF(
+    //     !ctx->boundBuffer(GL_TRANSFORM_FEEDBACK_BUFFER),
+    //     GL_INVALID_OPERATION);
+    SET_ERROR_IF(
+        !ctx->m_state->currentProgram(), GL_INVALID_OPERATION);
     ctx->m_glBeginTransformFeedback_enc(ctx, primitiveMode);
-    state->setTransformFeedbackActiveUnpaused(true);
+    state->setTransformFeedbackActive(true);
+    state->setTransformFeedbackUnpaused(true);
 }
 
 void GL2Encoder::s_glEndTransformFeedback(void* self) {
     GL2Encoder* ctx = (GL2Encoder*)self;
     GLClientState* state = ctx->m_state;
+    SET_ERROR_IF(!state->getTransformFeedbackActive(), GL_INVALID_OPERATION);
     ctx->m_glEndTransformFeedback_enc(ctx);
-    state->setTransformFeedbackActiveUnpaused(false);
+    state->setTransformFeedbackActive(false);
+    state->setTransformFeedbackUnpaused(false);
 }
 
 void GL2Encoder::s_glPauseTransformFeedback(void* self) {
     GL2Encoder* ctx = (GL2Encoder*)self;
     GLClientState* state = ctx->m_state;
+    SET_ERROR_IF(!state->getTransformFeedbackActive(), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!state->getTransformFeedbackUnpaused(), GL_INVALID_OPERATION);
     ctx->m_glPauseTransformFeedback_enc(ctx);
-    state->setTransformFeedbackActiveUnpaused(false);
+    state->setTransformFeedbackUnpaused(false);
 }
 
 void GL2Encoder::s_glResumeTransformFeedback(void* self) {
     GL2Encoder* ctx = (GL2Encoder*)self;
     GLClientState* state = ctx->m_state;
+    SET_ERROR_IF(!state->getTransformFeedbackActive(), GL_INVALID_OPERATION);
+    SET_ERROR_IF(state->getTransformFeedbackUnpaused(), GL_INVALID_OPERATION);
     ctx->m_glResumeTransformFeedback_enc(ctx);
-    state->setTransformFeedbackActiveUnpaused(true);
+    state->setTransformFeedbackUnpaused(true);
 }
 
 void GL2Encoder::s_glTexImage3D(void* self, GLenum target, GLint level, GLint internalFormat,
@@ -3943,6 +4195,10 @@
                  GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::pixelType(ctx, type), GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::pixelFormat(ctx, format), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::pixelSizedFormat(ctx, internalFormat, format, type), GL_INVALID_OPERATION);
+    SET_ERROR_IF(target == GL_TEXTURE_3D &&
+        ((format == GL_DEPTH_COMPONENT) ||
+         (format == GL_DEPTH_STENCIL)), GL_INVALID_OPERATION);
 
     // If unpack buffer is nonzero, verify unmapped state.
     SET_ERROR_IF(ctx->isBufferTargetMapped(GL_PIXEL_UNPACK_BUFFER), GL_INVALID_OPERATION);
@@ -3958,7 +4214,13 @@
     SET_ERROR_IF(width < 0 || height < 0 || depth < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(width > max_texture_size, GL_INVALID_VALUE);
     SET_ERROR_IF(height > max_texture_size, GL_INVALID_VALUE);
-    SET_ERROR_IF(depth > max_texture_size, GL_INVALID_VALUE);
+    if (target == GL_TEXTURE_3D) {
+        SET_ERROR_IF(depth > max_texture_size, GL_INVALID_VALUE);
+    } else {
+        GLint maxArrayTextureLayers;
+        ctx->glGetIntegerv(ctx, GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArrayTextureLayers);
+        SET_ERROR_IF(depth > maxArrayTextureLayers, GL_INVALID_VALUE);
+    }
     SET_ERROR_IF(width > max_3d_texture_size, GL_INVALID_VALUE);
     SET_ERROR_IF(height > max_3d_texture_size, GL_INVALID_VALUE);
     SET_ERROR_IF(depth > max_3d_texture_size, GL_INVALID_VALUE);
@@ -3966,12 +4228,12 @@
     // If unpack buffer is nonzero, verify buffer data fits and is evenly divisible by the type.
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
                  ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
-                 (ctx->m_state->pboNeededDataSize(width, height, depth, format, type, 0) >
+                 ((uintptr_t)data + ctx->m_state->pboNeededDataSize(width, height, depth, format, type, 0) >
                   ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size),
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
                  ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
-                 (ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size %
+                 ((uintptr_t)data %
                   glSizeof(type)),
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(state->isBoundTextureImmutableFormat(target), GL_INVALID_OPERATION);
@@ -3979,7 +4241,7 @@
     state->setBoundTextureInternalFormat(target, internalFormat);
     state->setBoundTextureFormat(target, format);
     state->setBoundTextureType(target, type);
-    state->setBoundTextureDims(target, level, width, height, depth);
+    state->setBoundTextureDims(target, target, level, width, height, depth);
 
     if (ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER)) {
         ctx->glTexImage3DOffsetAEMU(
@@ -4027,13 +4289,12 @@
     // If unpack buffer is nonzero, verify buffer data fits and is evenly divisible by the type.
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
                  ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
-                 (ctx->m_state->pboNeededDataSize(width, height, depth, format, type, 0) >
+                 ((uintptr_t)data + ctx->m_state->pboNeededDataSize(width, height, depth, format, type, 0) >
                   ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size),
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
                  ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
-                 (ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size %
-                  glSizeof(type)),
+                 ((uintptr_t)data % glSizeof(type)),
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(!ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) && !data, GL_INVALID_OPERATION);
     SET_ERROR_IF(xoffset < 0 || yoffset < 0 || zoffset < 0, GL_INVALID_VALUE);
@@ -4057,22 +4318,48 @@
     GL2Encoder* ctx = (GL2Encoder*)self;
     GLClientState* state = ctx->m_state;
 
+    SET_ERROR_IF(target != GL_TEXTURE_3D &&
+                 target != GL_TEXTURE_2D_ARRAY,
+                 GL_INVALID_ENUM);
     // Filter compressed formats support.
     SET_ERROR_IF(!GLESv2Validation::supportedCompressedFormat(ctx, internalformat), GL_INVALID_ENUM);
+    SET_ERROR_IF(target == GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM);
     // If unpack buffer is nonzero, verify unmapped state.
     SET_ERROR_IF(ctx->isBufferTargetMapped(GL_PIXEL_UNPACK_BUFFER), GL_INVALID_OPERATION);
     SET_ERROR_IF(width < 0 || height < 0 || depth < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(border, GL_INVALID_VALUE);
+
+    GLint max_texture_size;
+    GLint max_3d_texture_size;
+    ctx->glGetIntegerv(ctx, GL_MAX_TEXTURE_SIZE, &max_texture_size);
+    ctx->glGetIntegerv(ctx, GL_MAX_3D_TEXTURE_SIZE, &max_3d_texture_size);
+    SET_ERROR_IF(level < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(level > ilog2(max_texture_size), GL_INVALID_VALUE);
+    SET_ERROR_IF(level > ilog2(max_3d_texture_size), GL_INVALID_VALUE);
+
+    SET_ERROR_IF(width < 0 || height < 0 || depth < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(width > max_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(height > max_texture_size, GL_INVALID_VALUE);
+    if (target == GL_TEXTURE_3D) {
+        SET_ERROR_IF(depth > max_texture_size, GL_INVALID_VALUE);
+    } else {
+        GLint maxArrayTextureLayers;
+        ctx->glGetIntegerv(ctx, GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArrayTextureLayers);
+        SET_ERROR_IF(depth > maxArrayTextureLayers, GL_INVALID_VALUE);
+    }
+    SET_ERROR_IF(width > max_3d_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(height > max_3d_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(depth > max_3d_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(GLESTextureUtils::isAstcFormat(internalformat) && GL_TEXTURE_3D == target, GL_INVALID_OPERATION);
+
     // If unpack buffer is nonzero, verify buffer data fits.
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
                  ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
                  (imageSize > ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size),
                  GL_INVALID_OPERATION);
-    // TODO: Fix:
-    // If |imageSize| is too small for compressed dimensions.
-    // SET_ERROR_IF(GLESv2Validation::compressedTexImageSize(internalformat, width, height, depth) > imageSize, GL_INVALID_VALUE);
+    SET_ERROR_IF(!ctx->m_state->compressedTexImageSizeCompatible(internalformat, width, height, depth, imageSize), GL_INVALID_VALUE);
     state->setBoundTextureInternalFormat(target, (GLint)internalformat);
-    state->setBoundTextureDims(target, level, width, height, depth);
+    state->setBoundTextureDims(target, target, level, width, height, depth);
 
     if (ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER)) {
         ctx->glCompressedTexImage3DOffsetAEMU(
@@ -4091,6 +4378,7 @@
     GL2Encoder* ctx = (GL2Encoder*)self;
 
     SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(target == GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM);
     // If unpack buffer is nonzero, verify unmapped state.
     SET_ERROR_IF(ctx->isBufferTargetMapped(GL_PIXEL_UNPACK_BUFFER), GL_INVALID_OPERATION);
     SET_ERROR_IF(width < 0 || height < 0 || depth < 0, GL_INVALID_VALUE);
@@ -4101,6 +4389,52 @@
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(xoffset < 0 || yoffset < 0 || zoffset < 0, GL_INVALID_VALUE);
 
+    GLint max_texture_size;
+    GLint max_3d_texture_size;
+    ctx->glGetIntegerv(ctx, GL_MAX_TEXTURE_SIZE, &max_texture_size);
+    ctx->glGetIntegerv(ctx, GL_MAX_3D_TEXTURE_SIZE, &max_3d_texture_size);
+    SET_ERROR_IF(level < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(level > ilog2(max_texture_size), GL_INVALID_VALUE);
+    SET_ERROR_IF(level > ilog2(max_3d_texture_size), GL_INVALID_VALUE);
+    SET_ERROR_IF(width < 0 || height < 0 || depth < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(xoffset < 0 || yoffset < 0 || zoffset < 0, GL_INVALID_VALUE);
+    GLenum stateTarget = target;
+    if (target == GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
+        target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y ||
+        target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
+        stateTarget = GL_TEXTURE_CUBE_MAP;
+
+    GLuint tex = ctx->m_state->getBoundTexture(stateTarget);
+    GLsizei neededWidth = xoffset + width;
+    GLsizei neededHeight = yoffset + height;
+    GLsizei neededDepth = zoffset + depth;
+
+    SET_ERROR_IF(tex &&
+                 (neededWidth > ctx->m_state->queryTexWidth(level, tex) ||
+                  neededHeight > ctx->m_state->queryTexHeight(level, tex) ||
+                  neededDepth > ctx->m_state->queryTexDepth(level, tex)),
+                 GL_INVALID_VALUE);
+
+    GLint internalFormat = ctx->m_state->queryTexInternalFormat(tex);
+    SET_ERROR_IF(internalFormat != format, GL_INVALID_OPERATION);
+
+    GLint totalWidth = ctx->m_state->queryTexWidth(level, tex);
+    GLint totalHeight = ctx->m_state->queryTexHeight(level, tex);
+
+    if (GLESTextureUtils::isEtc2Format(internalFormat)) {
+        SET_ERROR_IF((width % 4) && (totalWidth != xoffset + width), GL_INVALID_OPERATION);
+        SET_ERROR_IF((height % 4) && (totalHeight != yoffset + height), GL_INVALID_OPERATION);
+        SET_ERROR_IF((xoffset % 4) || (yoffset % 4), GL_INVALID_OPERATION);
+    }
+
+    SET_ERROR_IF(totalWidth < xoffset + width, GL_INVALID_VALUE);
+    SET_ERROR_IF(totalHeight < yoffset + height, GL_INVALID_VALUE);
+
+    SET_ERROR_IF(!ctx->m_state->compressedTexImageSizeCompatible(internalFormat, width, height, depth, imageSize), GL_INVALID_VALUE);
+
     if (ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER)) {
         ctx->glCompressedTexSubImage3DOffsetAEMU(
                 ctx, target, level,
@@ -4125,7 +4459,27 @@
                  GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::pixelInternalFormat(internalformat), GL_INVALID_ENUM);
     SET_ERROR_IF(!state->getBoundTexture(target), GL_INVALID_OPERATION);
-    SET_ERROR_IF(levels < 1 || width < 1 || height < 1, GL_INVALID_VALUE);
+    SET_ERROR_IF(levels < 1 || width < 1 || height < 1 || depth < 1, GL_INVALID_VALUE);
+    GLint max_texture_size;
+    GLint max_3d_texture_size;
+    ctx->glGetIntegerv(ctx, GL_MAX_TEXTURE_SIZE, &max_texture_size);
+    ctx->glGetIntegerv(ctx, GL_MAX_3D_TEXTURE_SIZE, &max_3d_texture_size);
+    SET_ERROR_IF(width > max_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(height > max_texture_size, GL_INVALID_VALUE);
+    if (target == GL_TEXTURE_3D) {
+        SET_ERROR_IF(depth > max_texture_size, GL_INVALID_VALUE);
+    } else {
+        GLint maxArrayTextureLayers;
+        ctx->glGetIntegerv(ctx, GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArrayTextureLayers);
+        SET_ERROR_IF(depth > maxArrayTextureLayers, GL_INVALID_VALUE);
+    }
+
+    SET_ERROR_IF(width > max_3d_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(height > max_3d_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(depth > max_3d_texture_size, GL_INVALID_VALUE);
+
+    SET_ERROR_IF(GLESTextureUtils::isAstcFormat(internalformat) && GL_TEXTURE_3D == target, GL_INVALID_OPERATION);
+
     SET_ERROR_IF(target == GL_TEXTURE_3D && (levels > ilog2((uint32_t)std::max(width, std::max(height, depth))) + 1),
                  GL_INVALID_OPERATION);
     SET_ERROR_IF(target == GL_TEXTURE_2D_ARRAY && (levels > ilog2((uint32_t)std::max(width, height)) + 1),
@@ -4133,7 +4487,7 @@
     SET_ERROR_IF(state->isBoundTextureImmutableFormat(target), GL_INVALID_OPERATION);
 
     state->setBoundTextureInternalFormat(target, internalformat);
-    state->setBoundTextureDims(target, -1, width, height, depth);
+    state->setBoundTextureDims(target, target, -1, width, height, depth);
     state->setBoundTextureImmutableFormat(target);
     ctx->m_glTexStorage3D_enc(ctx, target, levels, internalformat, width, height, depth);
     state->setBoundTextureImmutableFormat(target);
@@ -4145,6 +4499,7 @@
     SET_ERROR_IF(!isValidDrawMode(mode), GL_INVALID_ENUM);
     SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(primcount < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(ctx->m_state->checkFramebufferCompleteness(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
 
     bool has_client_vertex_arrays = false;
     bool has_indirect_arrays = false;
@@ -4174,6 +4529,7 @@
     SET_ERROR_IF(primcount < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(!(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT), GL_INVALID_ENUM);
     SET_ERROR_IF(ctx->m_state->getTransformFeedbackActiveUnpaused(), GL_INVALID_OPERATION);
+    SET_ERROR_IF(ctx->m_state->checkFramebufferCompleteness(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
 
     bool has_client_vertex_arrays = false;
     bool has_indirect_arrays = false;
@@ -4266,6 +4622,7 @@
     SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(!(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT), GL_INVALID_ENUM);
     SET_ERROR_IF(ctx->m_state->getTransformFeedbackActiveUnpaused(), GL_INVALID_OPERATION);
+    SET_ERROR_IF(ctx->m_state->checkFramebufferCompleteness(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
 
     bool has_client_vertex_arrays = false;
     bool has_indirect_arrays = false;
@@ -4401,12 +4758,12 @@
 void GL2Encoder::s_glGetProgramBinary(void* self, GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary) {
     GL2Encoder *ctx = (GL2Encoder *)self;
 
-    SET_ERROR_IF(!ctx->m_shared->isProgram(program), GL_INVALID_OPERATION);
+    VALIDATE_PROGRAM_NAME(program);
 
     GLint linkStatus = 0;
-    ctx->glGetProgramiv(self, program, GL_LINK_STATUS, &linkStatus);
+    ctx->m_glGetProgramiv_enc(self, program, GL_LINK_STATUS, &linkStatus);
     GLint properLength = 0;
-    ctx->glGetProgramiv(self, program, GL_PROGRAM_BINARY_LENGTH, &properLength);
+    ctx->m_glGetProgramiv_enc(self, program, GL_PROGRAM_BINARY_LENGTH, &properLength);
 
     SET_ERROR_IF(!linkStatus, GL_INVALID_OPERATION);
     SET_ERROR_IF(bufSize < properLength, GL_INVALID_OPERATION);
@@ -4419,6 +4776,7 @@
 
     SET_ERROR_IF(!GLESv2Validation::readPixelsFormat(format), GL_INVALID_ENUM);
     SET_ERROR_IF(!GLESv2Validation::readPixelsType(type), GL_INVALID_ENUM);
+    SET_ERROR_IF(!(GLESv2Validation::pixelOp(format,type)),GL_INVALID_OPERATION);
     SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(ctx->isBufferTargetMapped(GL_PIXEL_PACK_BUFFER), GL_INVALID_OPERATION);
     SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_PACK_BUFFER) &&
@@ -4426,6 +4784,24 @@
                  (ctx->m_state->pboNeededDataSize(width, height, 1, format, type, 1) >
                   ctx->getBufferData(GL_PIXEL_PACK_BUFFER)->m_size),
                  GL_INVALID_OPERATION);
+    SET_ERROR_IF(ctx->s_glCheckFramebufferStatus(ctx, GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
+
+    // now is complete
+    // GL_INVALID_OPERATION is generated if GL_READ_FRAMEBUFFER_BINDING is nonzero, the read fbo is complete, and the value of
+    // GL_SAMPLE_BUFFERS for the read framebuffer is greater than zero
+    if (ctx->m_state->boundFramebuffer(GL_READ_FRAMEBUFFER) &&
+        ctx->s_glCheckFramebufferStatus(ctx, GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
+        FboFormatInfo resInfo;
+        ctx->m_state->getBoundFramebufferFormat(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, &resInfo);
+        if (resInfo.type == FBO_ATTACHMENT_RENDERBUFFER) {
+            SET_ERROR_IF(resInfo.rb_multisamples > 0, GL_INVALID_OPERATION);
+        }
+        if (resInfo.type == FBO_ATTACHMENT_TEXTURE) {
+            SET_ERROR_IF(resInfo.tex_multisamples > 0, GL_INVALID_OPERATION);
+        }
+    }
+
+
     /*
 GL_INVALID_OPERATION is generated if the readbuffer of the currently bound framebuffer is a fixed point normalized surface and format and type are neither GL_RGBA and GL_UNSIGNED_BYTE, respectively, nor the format/type pair returned by querying GL_IMPLEMENTATION_COLOR_READ_FORMAT and GL_IMPLEMENTATION_COLOR_READ_TYPE.
 
@@ -4462,6 +4838,8 @@
 void GL2Encoder::s_glEnable(void* self, GLenum what) {
     GL2Encoder *ctx = (GL2Encoder *)self;
 
+	SET_ERROR_IF(!GLESv2Validation::allowedEnable(ctx->majorVersion(), ctx->minorVersion(), what), GL_INVALID_ENUM);
+
     switch (what) {
     case GL_PRIMITIVE_RESTART_FIXED_INDEX:
         ctx->m_primitiveRestartEnabled = true;
@@ -4474,6 +4852,8 @@
 void GL2Encoder::s_glDisable(void* self, GLenum what) {
     GL2Encoder *ctx = (GL2Encoder *)self;
 
+	SET_ERROR_IF(!GLESv2Validation::allowedEnable(ctx->majorVersion(), ctx->minorVersion(), what), GL_INVALID_ENUM);
+
     switch (what) {
     case GL_PRIMITIVE_RESTART_FIXED_INDEX:
         ctx->m_primitiveRestartEnabled = false;
@@ -4486,7 +4866,18 @@
 void GL2Encoder::s_glClearBufferiv(void* self, GLenum buffer, GLint drawBuffer, const GLint * value) {
     GL2Encoder *ctx = (GL2Encoder *)self;
 
-    SET_ERROR_IF(buffer == GL_DEPTH || buffer == GL_DEPTH_STENCIL, GL_INVALID_ENUM);
+    SET_ERROR_IF(buffer != GL_COLOR && buffer != GL_STENCIL, GL_INVALID_ENUM);
+
+    GLint maxDrawBuffers;
+    ctx->glGetIntegerv(ctx, GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
+
+    SET_ERROR_IF(!value, GL_INVALID_VALUE);
+
+    if (buffer == GL_COLOR) {
+        SET_ERROR_IF(drawBuffer < 0 || drawBuffer>= maxDrawBuffers, GL_INVALID_VALUE);
+    } else {
+        SET_ERROR_IF(drawBuffer != 0, GL_INVALID_VALUE);
+    }
 
     ctx->m_glClearBufferiv_enc(ctx, buffer, drawBuffer, value);
 }
@@ -4494,7 +4885,12 @@
 void GL2Encoder::s_glClearBufferuiv(void* self, GLenum buffer, GLint drawBuffer, const GLuint * value) {
     GL2Encoder *ctx = (GL2Encoder *)self;
 
-    SET_ERROR_IF(buffer == GL_DEPTH || buffer == GL_STENCIL || buffer == GL_DEPTH_STENCIL, GL_INVALID_ENUM);
+    SET_ERROR_IF(buffer != GL_COLOR, GL_INVALID_ENUM);
+    SET_ERROR_IF(!value, GL_INVALID_VALUE);
+
+    GLint maxDrawBuffers;
+    ctx->glGetIntegerv(ctx, GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
+    SET_ERROR_IF(drawBuffer < 0 || drawBuffer>= maxDrawBuffers, GL_INVALID_VALUE);
 
     ctx->m_glClearBufferuiv_enc(ctx, buffer, drawBuffer, value);
 }
@@ -4502,11 +4898,31 @@
 void GL2Encoder::s_glClearBufferfv(void* self, GLenum buffer, GLint drawBuffer, const GLfloat * value) {
     GL2Encoder *ctx = (GL2Encoder *)self;
 
-    SET_ERROR_IF(buffer == GL_STENCIL || buffer == GL_DEPTH_STENCIL, GL_INVALID_ENUM);
+    SET_ERROR_IF(buffer != GL_COLOR && buffer != GL_DEPTH, GL_INVALID_ENUM);
+
+    SET_ERROR_IF(!value, GL_INVALID_VALUE);
+
+    GLint maxDrawBuffers;
+    ctx->glGetIntegerv(ctx, GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
+
+    if (buffer == GL_COLOR) {
+        SET_ERROR_IF(drawBuffer < 0 || drawBuffer>= maxDrawBuffers, GL_INVALID_VALUE);
+    } else {
+        SET_ERROR_IF(drawBuffer != 0, GL_INVALID_VALUE);
+    }
 
     ctx->m_glClearBufferfv_enc(ctx, buffer, drawBuffer, value);
 }
 
+void GL2Encoder::s_glClearBufferfi(void* self, GLenum buffer, GLint drawBuffer, float depth, int stencil) {
+    GL2Encoder *ctx = (GL2Encoder *)self;
+
+    SET_ERROR_IF(buffer != GL_DEPTH_STENCIL, GL_INVALID_ENUM);
+    SET_ERROR_IF(drawBuffer != 0, GL_INVALID_VALUE);
+
+    ctx->m_glClearBufferfi_enc(ctx, buffer, drawBuffer, depth, stencil);
+}
+
 void GL2Encoder::s_glBlitFramebuffer(void* self, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
     GL2Encoder *ctx = (GL2Encoder *)self;
     GLClientState* state = ctx->m_state;
@@ -4514,6 +4930,7 @@
     bool validateColor = mask & GL_COLOR_BUFFER_BIT;
     bool validateDepth = mask & GL_DEPTH_BUFFER_BIT;
     bool validateStencil = mask & GL_STENCIL_BUFFER_BIT;
+    bool validateDepthOrStencil = validateDepth || validateStencil;
 
     FboFormatInfo read_fbo_format_info;
     FboFormatInfo draw_fbo_format_info;
@@ -4521,6 +4938,13 @@
         state->getBoundFramebufferFormat(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, &read_fbo_format_info);
         state->getBoundFramebufferFormat(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, &draw_fbo_format_info);
 
+        if (read_fbo_format_info.type == FBO_ATTACHMENT_TEXTURE) {
+            SET_ERROR_IF(
+                GL_LINEAR == filter &&
+                GLESv2Validation::isIntegerFormat(read_fbo_format_info.tex_format),
+                    GL_INVALID_OPERATION);
+        }
+
         if (read_fbo_format_info.type == FBO_ATTACHMENT_TEXTURE &&
             draw_fbo_format_info.type == FBO_ATTACHMENT_TEXTURE) {
             SET_ERROR_IF(
@@ -4565,6 +4989,10 @@
         }
     }
 
+    if (validateDepthOrStencil) {
+        SET_ERROR_IF(filter != GL_NEAREST, GL_INVALID_OPERATION);
+    }
+
     state->getBoundFramebufferFormat(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, &draw_fbo_format_info);
     SET_ERROR_IF(
             draw_fbo_format_info.type == FBO_ATTACHMENT_RENDERBUFFER &&
@@ -4652,6 +5080,17 @@
                    GLESv2Validation::filterableTexFormat(ctx, internalformat)),
                  GL_INVALID_OPERATION);
 
+    GLenum stateTarget = target;
+    if (target == GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
+        target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y ||
+        target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y ||
+        target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
+        stateTarget = GL_TEXTURE_CUBE_MAP;
+
+    SET_ERROR_IF(!ctx->m_state->isBoundTextureComplete(stateTarget), GL_INVALID_OPERATION);
+
     if (target == GL_TEXTURE_2D) {
         ctx->override2DTextureTarget(target);
     }
@@ -4668,35 +5107,42 @@
     GLint maxCombinedUnits;
     ctx->glGetIntegerv(ctx, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxCombinedUnits);
     SET_ERROR_IF(unit >= maxCombinedUnits, GL_INVALID_VALUE);
-
-    ctx->doSamplerBindEncodeCached(unit, sampler);
-}
-
-void GL2Encoder::doSamplerBindEncodeCached(GLuint unit, GLuint sampler) {
-    if (m_state->isSamplerBindNoOp(unit, sampler)) return;
-    m_glBindSampler_enc(this, unit, sampler);
-    m_state->bindSampler(unit, sampler);
+    SET_ERROR_IF(!ctx->m_state->samplerExists(sampler), GL_INVALID_OPERATION);
+    if (ctx->m_state->isSamplerBindNoOp(unit, sampler)) return;
+    ctx->m_glBindSampler_enc(ctx, unit, sampler);
+    ctx->m_state->bindSampler(unit, sampler);
 }
 
 void GL2Encoder::s_glDeleteSamplers(void* self, GLsizei n, const GLuint* samplers) {
     GL2Encoder *ctx = (GL2Encoder *)self;
     ctx->m_state->onDeleteSamplers(n, samplers);
+    ctx->m_state->setExistence(GLClientState::ObjectType::Sampler, false, n, samplers);
     ctx->m_glDeleteSamplers_enc(ctx, n, samplers);
 }
 
 GLsync GL2Encoder::s_glFenceSync(void* self, GLenum condition, GLbitfield flags) {
     GL2Encoder *ctx = (GL2Encoder *)self;
+    RET_AND_SET_ERROR_IF(condition != GL_SYNC_GPU_COMMANDS_COMPLETE, GL_INVALID_ENUM, 0);
+    RET_AND_SET_ERROR_IF(flags != 0, GL_INVALID_VALUE, 0);
     uint64_t syncHandle = ctx->glFenceSyncAEMU(ctx, condition, flags);
-    return (GLsync)(uintptr_t)syncHandle;
+
+    GLsync res = (GLsync)(uintptr_t)syncHandle;
+    GLClientState::onFenceCreated(res);
+    return res;
 }
 
 GLenum GL2Encoder::s_glClientWaitSync(void* self, GLsync wait_on, GLbitfield flags, GLuint64 timeout) {
     GL2Encoder *ctx = (GL2Encoder *)self;
+    RET_AND_SET_ERROR_IF(!GLClientState::fenceExists(wait_on), GL_INVALID_VALUE, GL_WAIT_FAILED);
+    RET_AND_SET_ERROR_IF(flags && !(flags & GL_SYNC_FLUSH_COMMANDS_BIT), GL_INVALID_VALUE, GL_WAIT_FAILED);
     return ctx->glClientWaitSyncAEMU(ctx, (uint64_t)(uintptr_t)wait_on, flags, timeout);
 }
 
 void GL2Encoder::s_glWaitSync(void* self, GLsync wait_on, GLbitfield flags, GLuint64 timeout) {
     GL2Encoder *ctx = (GL2Encoder *)self;
+    SET_ERROR_IF(flags != 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(timeout != GL_TIMEOUT_IGNORED, GL_INVALID_VALUE);
+    SET_ERROR_IF(!GLClientState::fenceExists(wait_on), GL_INVALID_VALUE);
     ctx->glWaitSyncAEMU(ctx, (uint64_t)(uintptr_t)wait_on, flags, timeout);
 }
 
@@ -4705,6 +5151,8 @@
 
     if (!sync) return;
 
+    SET_ERROR_IF(!GLClientState::fenceExists(sync), GL_INVALID_VALUE);
+    GLClientState::onFenceDestroyed(sync);
     ctx->glDeleteSyncAEMU(ctx, (uint64_t)(uintptr_t)sync);
 }
 
@@ -4716,7 +5164,9 @@
 void GL2Encoder::s_glGetSynciv(void* self, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) {
     GL2Encoder *ctx = (GL2Encoder *)self;
 
+    SET_ERROR_IF(!GLESv2Validation::allowedGetSyncParam(pname), GL_INVALID_ENUM);
     SET_ERROR_IF(bufSize < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(!GLClientState::fenceExists(sync), GL_INVALID_VALUE);
 
     return ctx->glGetSyncivAEMU(ctx, (uint64_t)(uintptr_t)sync, pname, bufSize, length, values);
 }
@@ -4838,6 +5288,10 @@
 void GL2Encoder::s_glGetShaderiv(void* self, GLuint shader, GLenum pname, GLint* params) {
     GL2Encoder *ctx = (GL2Encoder *)self;
     ctx->m_glGetShaderiv_enc(self, shader, pname, params);
+
+    SET_ERROR_IF(!GLESv2Validation::allowedGetShader(pname), GL_INVALID_ENUM);
+    VALIDATE_SHADER_NAME(shader);
+	
     if (pname == GL_SHADER_SOURCE_LENGTH) {
         ShaderData* shaderData = ctx->m_shared->getShaderData(shader);
         if (shaderData) {
@@ -4893,7 +5347,8 @@
 
     // Phase 2: do glLinkProgram-related initialization for locationWorkARound
     GLint linkStatus = 0;
-    ctx->glGetProgramiv(self, res, GL_LINK_STATUS ,&linkStatus);
+    ctx->m_glGetProgramiv_enc(self, res, GL_LINK_STATUS ,&linkStatus);
+    ctx->m_shared->setProgramLinkStatus(res, linkStatus);
     if (!linkStatus) {
         ctx->m_shared->deleteShaderProgramDataById(spDataId);
         return -1;
@@ -4902,20 +5357,30 @@
     ctx->m_shared->associateGLShaderProgram(res, spDataId);
 
     GLint numUniforms = 0;
-    ctx->glGetProgramiv(self, res, GL_ACTIVE_UNIFORMS, &numUniforms);
-    ctx->m_shared->initShaderProgramData(res, numUniforms);
+    GLint numAttributes = 0;
+    ctx->m_glGetProgramiv_enc(self, res, GL_ACTIVE_UNIFORMS, &numUniforms);
+    ctx->m_glGetProgramiv_enc(self, res, GL_ACTIVE_ATTRIBUTES, &numAttributes);
+    ctx->m_shared->initShaderProgramData(res, numUniforms, numAttributes);
 
     GLint maxLength=0;
-    ctx->glGetProgramiv(self, res, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
+    ctx->m_glGetProgramiv_enc(self, res, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
 
     GLint size; GLenum uniformType; GLchar* name = new GLchar[maxLength + 1];
 
     for (GLint i = 0; i < numUniforms; ++i) {
-        ctx->glGetActiveUniform(self, res, i, maxLength, NULL, &size, &uniformType, name);
+        ctx->m_glGetActiveUniform_enc(self, res, i, maxLength, NULL, &size, &uniformType, name);
         GLint location = ctx->m_glGetUniformLocation_enc(self, res, name);
         ctx->m_shared->setShaderProgramIndexInfo(res, i, location, size, uniformType, name);
     }
 
+    GLint numBlocks;
+    ctx->m_glGetProgramiv_enc(ctx, res, GL_ACTIVE_UNIFORM_BLOCKS, &numBlocks);
+    ctx->m_shared->setActiveUniformBlockCountForProgram(res, numBlocks);
+
+    GLint tfVaryingsCount;
+    ctx->m_glGetProgramiv_enc(ctx, res, GL_TRANSFORM_FEEDBACK_VARYINGS, &tfVaryingsCount);
+    ctx->m_shared->setTransformFeedbackVaryingsCountForProgram(res, tfVaryingsCount);
+
     delete [] name;
 
     return res;
@@ -4924,22 +5389,19 @@
 void GL2Encoder::s_glProgramUniform1f(void* self, GLuint program, GLint location, GLfloat v0)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform1f_enc(self, program, hostLoc, v0);
+    ctx->m_glProgramUniform1f_enc(self, program, location, v0);
 }
 
 void GL2Encoder::s_glProgramUniform1fv(void* self, GLuint program, GLint location, GLsizei count, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform1fv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform1fv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform1i(void* self, GLuint program, GLint location, GLint v0)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform1i_enc(self, program, hostLoc, v0);
+    ctx->m_glProgramUniform1i_enc(self, program, location, v0);
 
     GLClientState* state = ctx->m_state;
     GLSharedGroupPtr shared = ctx->m_shared;
@@ -4957,15 +5419,13 @@
 void GL2Encoder::s_glProgramUniform1iv(void* self, GLuint program, GLint location, GLsizei count, const GLint *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform1iv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform1iv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform1ui(void* self, GLuint program, GLint location, GLuint v0)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform1ui_enc(self, program, hostLoc, v0);
+    ctx->m_glProgramUniform1ui_enc(self, program, location, v0);
 
     GLClientState* state = ctx->m_state;
     GLSharedGroupPtr shared = ctx->m_shared;
@@ -4983,201 +5443,176 @@
 void GL2Encoder::s_glProgramUniform1uiv(void* self, GLuint program, GLint location, GLsizei count, const GLuint *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform1uiv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform1uiv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform2f(void* self, GLuint program, GLint location, GLfloat v0, GLfloat v1)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform2f_enc(self, program, hostLoc, v0, v1);
+    ctx->m_glProgramUniform2f_enc(self, program, location, v0, v1);
 }
 
 void GL2Encoder::s_glProgramUniform2fv(void* self, GLuint program, GLint location, GLsizei count, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform2fv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform2fv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform2i(void* self, GLuint program, GLint location, GLint v0, GLint v1)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform2i_enc(self, program, hostLoc, v0, v1);
+    ctx->m_glProgramUniform2i_enc(self, program, location, v0, v1);
 }
 
 void GL2Encoder::s_glProgramUniform2iv(void* self, GLuint program, GLint location, GLsizei count, const GLint *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform2iv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform2iv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform2ui(void* self, GLuint program, GLint location, GLint v0, GLuint v1)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform2ui_enc(self, program, hostLoc, v0, v1);
+    ctx->m_glProgramUniform2ui_enc(self, program, location, v0, v1);
 }
 
 void GL2Encoder::s_glProgramUniform2uiv(void* self, GLuint program, GLint location, GLsizei count, const GLuint *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform2uiv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform2uiv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform3f(void* self, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform3f_enc(self, program, hostLoc, v0, v1, v2);
+    ctx->m_glProgramUniform3f_enc(self, program, location, v0, v1, v2);
 }
 
 void GL2Encoder::s_glProgramUniform3fv(void* self, GLuint program, GLint location, GLsizei count, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform3fv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform3fv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform3i(void* self, GLuint program, GLint location, GLint v0, GLint v1, GLint v2)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform3i_enc(self, program, hostLoc, v0, v1, v2);
+    ctx->m_glProgramUniform3i_enc(self, program, location, v0, v1, v2);
 }
 
 void GL2Encoder::s_glProgramUniform3iv(void* self, GLuint program, GLint location, GLsizei count, const GLint *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform3iv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform3iv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform3ui(void* self, GLuint program, GLint location, GLint v0, GLint v1, GLuint v2)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform3ui_enc(self, program, hostLoc, v0, v1, v2);
+    ctx->m_glProgramUniform3ui_enc(self, program, location, v0, v1, v2);
 }
 
 void GL2Encoder::s_glProgramUniform3uiv(void* self, GLuint program, GLint location, GLsizei count, const GLuint *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform3uiv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform3uiv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform4f(void* self, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform4f_enc(self, program, hostLoc, v0, v1, v2, v3);
+    ctx->m_glProgramUniform4f_enc(self, program, location, v0, v1, v2, v3);
 }
 
 void GL2Encoder::s_glProgramUniform4fv(void* self, GLuint program, GLint location, GLsizei count, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform4fv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform4fv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform4i(void* self, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform4i_enc(self, program, hostLoc, v0, v1, v2, v3);
+    ctx->m_glProgramUniform4i_enc(self, program, location, v0, v1, v2, v3);
 }
 
 void GL2Encoder::s_glProgramUniform4iv(void* self, GLuint program, GLint location, GLsizei count, const GLint *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform4iv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform4iv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniform4ui(void* self, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLuint v3)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform4ui_enc(self, program, hostLoc, v0, v1, v2, v3);
+    ctx->m_glProgramUniform4ui_enc(self, program, location, v0, v1, v2, v3);
 }
 
 void GL2Encoder::s_glProgramUniform4uiv(void* self, GLuint program, GLint location, GLsizei count, const GLuint *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniform4uiv_enc(self, program, hostLoc, count, value);
+    ctx->m_glProgramUniform4uiv_enc(self, program, location, count, value);
 }
 
 void GL2Encoder::s_glProgramUniformMatrix2fv(void* self, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniformMatrix2fv_enc(self, program, hostLoc, count, transpose, value);
+    ctx->m_glProgramUniformMatrix2fv_enc(self, program, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glProgramUniformMatrix2x3fv(void* self, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniformMatrix2x3fv_enc(self, program, hostLoc, count, transpose, value);
+    ctx->m_glProgramUniformMatrix2x3fv_enc(self, program, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glProgramUniformMatrix2x4fv(void* self, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniformMatrix2x4fv_enc(self, program, hostLoc, count, transpose, value);
+    ctx->m_glProgramUniformMatrix2x4fv_enc(self, program, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glProgramUniformMatrix3fv(void* self, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniformMatrix3fv_enc(self, program, hostLoc, count, transpose, value);
+    ctx->m_glProgramUniformMatrix3fv_enc(self, program, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glProgramUniformMatrix3x2fv(void* self, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniformMatrix3x2fv_enc(self, program, hostLoc, count, transpose, value);
+    ctx->m_glProgramUniformMatrix3x2fv_enc(self, program, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glProgramUniformMatrix3x4fv(void* self, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniformMatrix3x4fv_enc(self, program, hostLoc, count, transpose, value);
+    ctx->m_glProgramUniformMatrix3x4fv_enc(self, program, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glProgramUniformMatrix4fv(void* self, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniformMatrix4fv_enc(self, program, hostLoc, count, transpose, value);
+    ctx->m_glProgramUniformMatrix4fv_enc(self, program, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glProgramUniformMatrix4x2fv(void* self, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniformMatrix4x2fv_enc(self, program, hostLoc, count, transpose, value);
+    ctx->m_glProgramUniformMatrix4x2fv_enc(self, program, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glProgramUniformMatrix4x3fv(void* self, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 {
     GL2Encoder *ctx = (GL2Encoder*)self;
-    GLint hostLoc = location;
-    ctx->m_glProgramUniformMatrix4x3fv_enc(self, program, hostLoc, count, transpose, value);
+    ctx->m_glProgramUniformMatrix4x3fv_enc(self, program, location, count, transpose, value);
 }
 
 void GL2Encoder::s_glProgramParameteri(void* self, GLuint program, GLenum pname, GLint value) {
     GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_PROGRAM_NAME(program);
+    SET_ERROR_IF(pname != GL_PROGRAM_BINARY_RETRIEVABLE_HINT && pname != GL_PROGRAM_SEPARABLE, GL_INVALID_ENUM);
+    SET_ERROR_IF(value != GL_FALSE && value != GL_TRUE, GL_INVALID_VALUE);
     ctx->m_glProgramParameteri_enc(self, program, pname, value);
 }
 
@@ -5368,6 +5803,7 @@
     SET_ERROR_IF(hasClientArrays, GL_INVALID_OPERATION);
     SET_ERROR_IF(!state->currentVertexArrayObject(), GL_INVALID_OPERATION);
     SET_ERROR_IF(!ctx->boundBuffer(GL_DRAW_INDIRECT_BUFFER), GL_INVALID_OPERATION);
+    SET_ERROR_IF(ctx->m_state->checkFramebufferCompleteness(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
 
     GLuint indirectStructSize = glUtilsIndirectStructSize(INDIRECT_COMMAND_DRAWARRAYS);
     if (ctx->boundBuffer(GL_DRAW_INDIRECT_BUFFER)) {
@@ -5398,6 +5834,7 @@
     SET_ERROR_IF(!ctx->boundBuffer(GL_DRAW_INDIRECT_BUFFER), GL_INVALID_OPERATION);
 
     SET_ERROR_IF(ctx->m_state->getTransformFeedbackActiveUnpaused(), GL_INVALID_OPERATION);
+    SET_ERROR_IF(ctx->m_state->checkFramebufferCompleteness(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
 
     GLuint indirectStructSize = glUtilsIndirectStructSize(INDIRECT_COMMAND_DRAWELEMENTS);
     if (ctx->boundBuffer(GL_DRAW_INDIRECT_BUFFER)) {
@@ -5428,7 +5865,7 @@
     SET_ERROR_IF(samples > max_samples, GL_INVALID_OPERATION);
 
     state->setBoundTextureInternalFormat(target, internalformat);
-    state->setBoundTextureDims(target, 0, width, height, 1);
+    state->setBoundTextureDims(target, target, 0, width, height, 1);
     state->setBoundTextureImmutableFormat(target);
     state->setBoundTextureSamples(target, samples);
 
@@ -5468,15 +5905,35 @@
 
 void GL2Encoder::s_glInvalidateFramebuffer(void* self, GLenum target, GLsizei numAttachments, const GLenum *attachments) {
     GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF((target != GL_FRAMEBUFFER) &&
+                 (target != GL_READ_FRAMEBUFFER) &&
+                 (target != GL_DRAW_FRAMEBUFFER), GL_INVALID_ENUM);
     SET_ERROR_IF(numAttachments < 0, GL_INVALID_VALUE);
+
+    GLint maxColorAttachments;
+    ctx->glGetIntegerv(ctx, GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments);
+    for (GLsizei i = 0; i < numAttachments; ++i) {
+        if (attachments[i] != GL_DEPTH_ATTACHMENT && attachments[i] != GL_STENCIL_ATTACHMENT && attachments[i] != GL_DEPTH_STENCIL_ATTACHMENT) {
+            SET_ERROR_IF(attachments[i] >= GL_COLOR_ATTACHMENT0 + maxColorAttachments, GL_INVALID_OPERATION);
+        }
+    }
+
     ctx->m_glInvalidateFramebuffer_enc(ctx, target, numAttachments, attachments);
 }
 
 void GL2Encoder::s_glInvalidateSubFramebuffer(void* self, GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) {
     GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(target != GL_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER, GL_INVALID_ENUM);
     SET_ERROR_IF(numAttachments < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(width < 0, GL_INVALID_VALUE);
     SET_ERROR_IF(height < 0, GL_INVALID_VALUE);
+    GLint maxColorAttachments;
+    ctx->glGetIntegerv(ctx, GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments);
+    for (GLsizei i = 0; i < numAttachments; ++i) {
+        if (attachments[i] != GL_DEPTH_ATTACHMENT && attachments[i] != GL_STENCIL_ATTACHMENT && attachments[i] != GL_DEPTH_STENCIL_ATTACHMENT) {
+            SET_ERROR_IF(attachments[i] >= GL_COLOR_ATTACHMENT0 + maxColorAttachments, GL_INVALID_OPERATION);
+        }
+    }
     ctx->m_glInvalidateSubFramebuffer_enc(ctx, target, numAttachments, attachments, x, y, width, height);
 }
 
@@ -5491,3 +5948,583 @@
     ctx->m_glDispatchComputeIndirect_enc(ctx, indirect);
     ctx->m_state->postDispatchCompute();
 }
+
+void GL2Encoder::s_glGenTransformFeedbacks(void* self, GLsizei n, GLuint* ids) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    ctx->m_glGenTransformFeedbacks_enc(ctx, n, ids);
+    ctx->m_state->setExistence(GLClientState::ObjectType::TransformFeedback, true, n, ids);
+}
+
+void GL2Encoder::s_glDeleteTransformFeedbacks(void* self, GLsizei n, const GLuint* ids) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(ctx->m_state->getTransformFeedbackActive(), GL_INVALID_OPERATION);
+
+    ctx->m_state->setExistence(GLClientState::ObjectType::TransformFeedback, false, n, ids);
+    ctx->m_glDeleteTransformFeedbacks_enc(ctx, n, ids);
+}
+
+void GL2Encoder::s_glGenSamplers(void* self, GLsizei n, GLuint* ids) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    ctx->m_glGenSamplers_enc(ctx, n, ids);
+    ctx->m_state->setExistence(GLClientState::ObjectType::Sampler, true, n, ids);
+}
+
+void GL2Encoder::s_glGenQueries(void* self, GLsizei n, GLuint* ids) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    ctx->m_glGenQueries_enc(ctx, n, ids);
+    ctx->m_state->setExistence(GLClientState::ObjectType::Query, true, n, ids);
+}
+
+void GL2Encoder::s_glDeleteQueries(void* self, GLsizei n, const GLuint* ids) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    ctx->m_state->setExistence(GLClientState::ObjectType::Query, false, n, ids);
+    ctx->m_glDeleteQueries_enc(ctx, n, ids);
+}
+
+void GL2Encoder::s_glBindTransformFeedback(void* self, GLenum target, GLuint id) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(GL_TRANSFORM_FEEDBACK != target, GL_INVALID_ENUM);
+    SET_ERROR_IF(ctx->m_state->getTransformFeedbackActiveUnpaused(), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!ctx->m_state->tryBind(target, id), GL_INVALID_OPERATION);
+    ctx->m_glBindTransformFeedback_enc(ctx, target, id);
+}
+
+void GL2Encoder::s_glBeginQuery(void* self, GLenum target, GLuint query) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!GLESv2Validation::allowedQueryTarget(target), GL_INVALID_ENUM);
+
+    if (target != GL_ANY_SAMPLES_PASSED_CONSERVATIVE &&
+        target != GL_ANY_SAMPLES_PASSED) {
+        SET_ERROR_IF(ctx->m_state->isQueryBound(target), GL_INVALID_OPERATION);
+    } else {
+        SET_ERROR_IF(ctx->m_state->isQueryBound(GL_ANY_SAMPLES_PASSED_CONSERVATIVE), GL_INVALID_OPERATION);
+        SET_ERROR_IF(ctx->m_state->isQueryBound(GL_ANY_SAMPLES_PASSED), GL_INVALID_OPERATION);
+    }
+
+    GLenum lastTarget = ctx->m_state->getLastQueryTarget(query);
+
+    if (lastTarget) {
+        SET_ERROR_IF(target != lastTarget, GL_INVALID_OPERATION);
+    }
+
+    SET_ERROR_IF(!query, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!ctx->m_state->tryBind(target, query), GL_INVALID_OPERATION);
+    ctx->m_state->setLastQueryTarget(target, query);
+    ctx->m_glBeginQuery_enc(ctx, target, query);
+}
+
+void GL2Encoder::s_glEndQuery(void* self, GLenum target) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!GLESv2Validation::allowedQueryTarget(target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!ctx->m_state->isBoundTargetValid(target), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!ctx->m_state->tryBind(target, 0), GL_INVALID_OPERATION);
+    ctx->m_glEndQuery_enc(ctx, target);
+}
+
+void GL2Encoder::s_glClear(void* self, GLbitfield mask) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+
+    GLbitfield allowed_bits = GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
+    GLbitfield has_disallowed_bits = (mask & ~allowed_bits);
+    SET_ERROR_IF(has_disallowed_bits, GL_INVALID_VALUE);
+
+    ctx->m_glClear_enc(ctx, mask);
+}
+
+void GL2Encoder::s_glCopyTexSubImage2D(void *self , GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
+    SET_ERROR_IF(level < 0, GL_INVALID_VALUE);
+    GLint max_texture_size;
+    GLint max_cube_map_texture_size;
+    ctx->glGetIntegerv(ctx, GL_MAX_TEXTURE_SIZE, &max_texture_size);
+    ctx->glGetIntegerv(ctx, GL_MAX_CUBE_MAP_TEXTURE_SIZE, &max_cube_map_texture_size);
+    SET_ERROR_IF(level > ilog2(max_texture_size), GL_INVALID_VALUE);
+    SET_ERROR_IF((target == GL_TEXTURE_CUBE_MAP) &&
+                 (level > ilog2(max_cube_map_texture_size)), GL_INVALID_VALUE);
+    SET_ERROR_IF(xoffset < 0 || yoffset < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(width > max_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(height > max_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && width > max_cube_map_texture_size, GL_INVALID_VALUE);
+    SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && height > max_cube_map_texture_size, GL_INVALID_VALUE);
+    GLuint tex = ctx->m_state->getBoundTexture(target);
+    GLsizei neededWidth = xoffset + width;
+    GLsizei neededHeight = yoffset + height;
+    SET_ERROR_IF(tex &&
+                 (neededWidth > ctx->m_state->queryTexWidth(level, tex) ||
+                  neededHeight > ctx->m_state->queryTexHeight(level, tex)),
+                 GL_INVALID_VALUE);
+    SET_ERROR_IF(ctx->glCheckFramebufferStatus(ctx, GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
+                 GL_INVALID_FRAMEBUFFER_OPERATION);
+
+    ctx->m_glCopyTexSubImage2D_enc(ctx, target, level, xoffset, yoffset, x, y, width, height);
+}
+
+void GL2Encoder::s_glCopyTexSubImage3D(void *self , GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(target != GL_TEXTURE_3D &&
+                 target != GL_TEXTURE_2D_ARRAY,
+                 GL_INVALID_ENUM);
+    GLint max_texture_size;
+    GLint max_3d_texture_size;
+    ctx->glGetIntegerv(ctx, GL_MAX_TEXTURE_SIZE, &max_texture_size);
+    ctx->glGetIntegerv(ctx, GL_MAX_3D_TEXTURE_SIZE, &max_3d_texture_size);
+    SET_ERROR_IF(level < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(level > ilog2(max_texture_size), GL_INVALID_VALUE);
+    SET_ERROR_IF(level > ilog2(max_3d_texture_size), GL_INVALID_VALUE);
+    SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(xoffset < 0 || yoffset < 0 || zoffset < 0, GL_INVALID_VALUE);
+    GLuint tex = ctx->m_state->getBoundTexture(target);
+    GLsizei neededWidth = xoffset + width;
+    GLsizei neededHeight = yoffset + height;
+    GLsizei neededDepth = zoffset + 1;
+    SET_ERROR_IF(tex &&
+                 (neededWidth > ctx->m_state->queryTexWidth(level, tex) ||
+                  neededHeight > ctx->m_state->queryTexHeight(level, tex) ||
+                  neededDepth > ctx->m_state->queryTexDepth(level, tex)),
+                 GL_INVALID_VALUE);
+    SET_ERROR_IF(ctx->glCheckFramebufferStatus(ctx, GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
+                 GL_INVALID_FRAMEBUFFER_OPERATION);
+
+    ctx->m_glCopyTexSubImage3D_enc(ctx, target, level, xoffset, yoffset, zoffset, x, y, width, height);
+}
+
+void GL2Encoder::s_glCompileShader(void* self, GLuint shader) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    bool isShaderOrProgramObject =
+        ctx->m_shared->isShaderOrProgramObject(shader);
+    bool isShader =
+        ctx->m_shared->isShader(shader);
+
+    SET_ERROR_IF(isShaderOrProgramObject && !isShader, GL_INVALID_OPERATION);
+    SET_ERROR_IF(!isShaderOrProgramObject && !isShader, GL_INVALID_VALUE);
+
+    ctx->m_glCompileShader_enc(ctx, shader);
+}
+
+void GL2Encoder::s_glValidateProgram(void* self, GLuint program ) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+
+    VALIDATE_PROGRAM_NAME(program);
+
+    ctx->m_glValidateProgram_enc(self, program);
+}
+
+void GL2Encoder::s_glProgramBinary(void *self , GLuint program, GLenum binaryFormat, const void* binary, GLsizei length) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+
+    VALIDATE_PROGRAM_NAME(program);
+
+    SET_ERROR_IF(~0 == binaryFormat, GL_INVALID_ENUM);
+
+    ctx->m_glProgramBinary_enc(self, program, binaryFormat, binary, length);
+}
+
+void GL2Encoder::s_glGetSamplerParameterfv(void *self, GLuint sampler, GLenum pname, GLfloat* params) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+
+    SET_ERROR_IF(!ctx->m_state->samplerExists(sampler), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!GLESv2Validation::samplerParams(ctx, pname), GL_INVALID_ENUM);
+
+    if (!params) return;
+
+    ctx->m_glGetSamplerParameterfv_enc(ctx, sampler, pname, params);
+}
+
+void GL2Encoder::s_glGetSamplerParameteriv(void *self, GLuint sampler, GLenum pname, GLint* params) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!ctx->m_state->samplerExists(sampler), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!GLESv2Validation::samplerParams(ctx, pname), GL_INVALID_ENUM);
+
+    if (!params) return;
+
+    ctx->m_glGetSamplerParameteriv_enc(ctx, sampler, pname, params);
+}
+
+void GL2Encoder::s_glSamplerParameterf(void *self , GLuint sampler, GLenum pname, GLfloat param) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!ctx->m_state->samplerExists(sampler), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!GLESv2Validation::samplerParams(ctx, pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParamValue(ctx, pname, (GLint)param, param, (GLenum)param), GL_INVALID_ENUM);
+
+    ctx->m_glSamplerParameterf_enc(ctx, sampler, pname, param);
+}
+
+void GL2Encoder::s_glSamplerParameteri(void *self , GLuint sampler, GLenum pname, GLint param) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!ctx->m_state->samplerExists(sampler), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!GLESv2Validation::samplerParams(ctx, pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::textureParamValue(ctx, pname, param, (GLfloat)param, (GLenum)param), GL_INVALID_ENUM);
+
+    ctx->m_glSamplerParameteri_enc(ctx, sampler, pname, param);
+}
+
+void GL2Encoder::s_glSamplerParameterfv(void *self , GLuint sampler, GLenum pname, const GLfloat* params) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!ctx->m_state->samplerExists(sampler), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!GLESv2Validation::samplerParams(ctx, pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(!params, GL_INVALID_VALUE);
+    GLfloat param = *params;
+    SET_ERROR_IF(!GLESv2Validation::textureParamValue(ctx, pname, (GLint)param, param, (GLenum)param), GL_INVALID_ENUM);
+
+    ctx->m_glSamplerParameterfv_enc(ctx, sampler, pname, params);
+}
+
+void GL2Encoder::s_glSamplerParameteriv(void *self , GLuint sampler, GLenum pname, const GLint* params) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!ctx->m_state->samplerExists(sampler), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!GLESv2Validation::samplerParams(ctx, pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(!params, GL_INVALID_VALUE);
+    GLint param = *params;
+    SET_ERROR_IF(!GLESv2Validation::textureParamValue(ctx, pname, (GLint)param, param, (GLenum)param), GL_INVALID_ENUM);
+
+    ctx->m_glSamplerParameteriv_enc(ctx, sampler, pname, params);
+}
+
+int GL2Encoder::s_glGetAttribLocation(void *self , GLuint program, const GLchar* name) {
+    GL2Encoder *ctx = (GL2Encoder*)self;
+
+    bool isShaderOrProgramObject =
+        ctx->m_shared->isShaderOrProgramObject(program);
+    bool isProgram =
+        ctx->m_shared->isProgram(program);
+
+    RET_AND_SET_ERROR_IF(!isShaderOrProgramObject, GL_INVALID_VALUE, -1);
+    RET_AND_SET_ERROR_IF(!isProgram, GL_INVALID_OPERATION, -1);
+    RET_AND_SET_ERROR_IF(!ctx->m_shared->getProgramLinkStatus(program), GL_INVALID_OPERATION, -1);
+
+    return ctx->m_glGetAttribLocation_enc(ctx, program, name);
+}
+
+void GL2Encoder::s_glBindAttribLocation(void *self , GLuint program, GLuint index, const GLchar* name) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+
+    VALIDATE_PROGRAM_NAME(program);
+
+    GLint maxVertexAttribs;
+    ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
+    SET_ERROR_IF(!(index < maxVertexAttribs), GL_INVALID_VALUE);
+    SET_ERROR_IF(index > maxVertexAttribs, GL_INVALID_VALUE);
+    SET_ERROR_IF(name && !strncmp("gl_", name, 3), GL_INVALID_OPERATION);
+
+    ctx->m_glBindAttribLocation_enc(ctx, program, index, name);
+}
+
+// TODO-SLOW
+void GL2Encoder::s_glUniformBlockBinding(void *self , GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+
+    VALIDATE_PROGRAM_NAME(program);
+    SET_ERROR_IF(uniformBlockIndex >= ctx->m_shared->getActiveUniformBlockCount(program), GL_INVALID_VALUE);
+
+    GLint maxUniformBufferBindings;
+    ctx->glGetIntegerv(ctx, GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings);
+    SET_ERROR_IF(uniformBlockBinding >= maxUniformBufferBindings, GL_INVALID_VALUE);
+
+    ctx->m_glUniformBlockBinding_enc(ctx, program, uniformBlockIndex, uniformBlockBinding);
+}
+
+void GL2Encoder::s_glGetTransformFeedbackVarying(void *self , GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLsizei* size, GLenum* type, char* name) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+
+    VALIDATE_PROGRAM_NAME(program);
+    SET_ERROR_IF(!ctx->m_shared->getProgramLinkStatus(program), GL_INVALID_OPERATION);
+    SET_ERROR_IF(index >= ctx->m_shared->getTransformFeedbackVaryingsCountForProgram(program), GL_INVALID_VALUE);
+
+    ctx->m_glGetTransformFeedbackVarying_enc(ctx, program, index, bufSize, length, size, type, name);
+}
+
+void GL2Encoder::s_glScissor(void *self , GLint x, GLint y, GLsizei width, GLsizei height) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
+    ctx->m_glScissor_enc(ctx, x, y, width, height);
+}
+
+void GL2Encoder::s_glDepthFunc(void *self , GLenum func) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        (func != GL_NEVER) &&
+        (func != GL_ALWAYS) &&
+        (func != GL_LESS) &&
+        (func != GL_LEQUAL) &&
+        (func != GL_EQUAL) &&
+        (func != GL_GREATER) &&
+        (func != GL_GEQUAL) &&
+        (func != GL_NOTEQUAL),
+        GL_INVALID_ENUM);
+    ctx->m_glDepthFunc_enc(ctx, func);
+}
+
+void GL2Encoder::s_glViewport(void *self , GLint x, GLint y, GLsizei width, GLsizei height) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
+    ctx->m_glViewport_enc(ctx, x, y, width, height);
+}
+
+void GL2Encoder::s_glStencilFunc(void *self , GLenum func, GLint ref, GLuint mask) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!GLESv2Validation::allowedFunc(func), GL_INVALID_ENUM);
+    ctx->m_glStencilFunc_enc(ctx, func, ref, mask);
+}
+
+void GL2Encoder::s_glStencilFuncSeparate(void *self , GLenum face, GLenum func, GLint ref, GLuint mask) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!GLESv2Validation::allowedFace(face) || !GLESv2Validation::allowedFunc(func), GL_INVALID_ENUM);
+    ctx->m_glStencilFuncSeparate_enc(ctx, face, func, ref, mask);
+}
+
+void GL2Encoder::s_glStencilOp(void *self , GLenum fail, GLenum zfail, GLenum zpass) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        !GLESv2Validation::allowedStencilOp(fail) ||
+        !GLESv2Validation::allowedStencilOp(zfail) ||
+        !GLESv2Validation::allowedStencilOp(zpass),
+        GL_INVALID_ENUM);
+    ctx->m_glStencilOp_enc(ctx, fail, zfail, zpass);
+}
+
+void GL2Encoder::s_glStencilOpSeparate(void *self , GLenum face, GLenum fail, GLenum zfail, GLenum zpass) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        !GLESv2Validation::allowedFace(face) ||
+        !GLESv2Validation::allowedStencilOp(fail) ||
+        !GLESv2Validation::allowedStencilOp(zfail) ||
+        !GLESv2Validation::allowedStencilOp(zpass),
+        GL_INVALID_ENUM);
+    ctx->m_glStencilOpSeparate_enc(ctx, face, fail, zfail, zpass);
+}
+
+void GL2Encoder::s_glStencilMaskSeparate(void *self , GLenum face, GLuint mask) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        !GLESv2Validation::allowedFace(face),
+        GL_INVALID_ENUM);
+    ctx->m_glStencilMaskSeparate_enc(ctx, face, mask);
+}
+
+void GL2Encoder::s_glBlendEquation(void *self , GLenum mode) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        !GLESv2Validation::allowedBlendEquation(mode),
+        GL_INVALID_ENUM);
+    ctx->m_glBlendEquation_enc(ctx, mode);
+}
+
+void GL2Encoder::s_glBlendEquationSeparate(void *self , GLenum modeRGB, GLenum modeAlpha) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        !GLESv2Validation::allowedBlendEquation(modeRGB) ||
+        !GLESv2Validation::allowedBlendEquation(modeAlpha),
+        GL_INVALID_ENUM);
+    ctx->m_glBlendEquationSeparate_enc(ctx, modeRGB, modeAlpha);
+}
+
+void GL2Encoder::s_glBlendFunc(void *self , GLenum sfactor, GLenum dfactor) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        !GLESv2Validation::allowedBlendFunc(sfactor) ||
+        !GLESv2Validation::allowedBlendFunc(dfactor),
+        GL_INVALID_ENUM);
+    ctx->m_glBlendFunc_enc(ctx, sfactor, dfactor);
+}
+
+void GL2Encoder::s_glBlendFuncSeparate(void *self , GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        !GLESv2Validation::allowedBlendFunc(srcRGB) ||
+        !GLESv2Validation::allowedBlendFunc(dstRGB) ||
+        !GLESv2Validation::allowedBlendFunc(srcAlpha) ||
+        !GLESv2Validation::allowedBlendFunc(dstAlpha),
+        GL_INVALID_ENUM);
+    ctx->m_glBlendFuncSeparate_enc(ctx, srcRGB, dstRGB, srcAlpha, dstAlpha);
+}
+
+void GL2Encoder::s_glCullFace(void *self , GLenum mode) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        !GLESv2Validation::allowedCullFace(mode),
+        GL_INVALID_ENUM);
+    ctx->m_glCullFace_enc(ctx, mode);
+}
+
+void GL2Encoder::s_glFrontFace(void *self , GLenum mode) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(
+        !GLESv2Validation::allowedFrontFace(mode),
+        GL_INVALID_ENUM);
+    ctx->m_glFrontFace_enc(ctx, mode);
+}
+
+void GL2Encoder::s_glLineWidth(void *self , GLfloat width) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(width <= 0.0f, GL_INVALID_VALUE);
+    ctx->m_glLineWidth_enc(ctx, width);
+}
+
+void GL2Encoder::s_glVertexAttrib1f(void *self , GLuint indx, GLfloat x) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(indx);
+    ctx->m_glVertexAttrib1f_enc(ctx, indx, x);
+}
+
+void GL2Encoder::s_glVertexAttrib2f(void *self , GLuint indx, GLfloat x, GLfloat y) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(indx);
+    ctx->m_glVertexAttrib2f_enc(ctx, indx, x, y);
+}
+
+void GL2Encoder::s_glVertexAttrib3f(void *self , GLuint indx, GLfloat x, GLfloat y, GLfloat z) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(indx);
+    ctx->m_glVertexAttrib3f_enc(ctx, indx, x, y, z);
+}
+
+void GL2Encoder::s_glVertexAttrib4f(void *self , GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(indx);
+    ctx->m_glVertexAttrib4f_enc(ctx, indx, x, y, z, w);
+}
+
+void GL2Encoder::s_glVertexAttrib1fv(void *self , GLuint indx, const GLfloat* values) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(indx);
+    ctx->m_glVertexAttrib1fv_enc(ctx, indx, values);
+}
+
+void GL2Encoder::s_glVertexAttrib2fv(void *self , GLuint indx, const GLfloat* values) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(indx);
+    ctx->m_glVertexAttrib2fv_enc(ctx, indx, values);
+}
+
+void GL2Encoder::s_glVertexAttrib3fv(void *self , GLuint indx, const GLfloat* values) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(indx);
+    ctx->m_glVertexAttrib3fv_enc(ctx, indx, values);
+}
+
+void GL2Encoder::s_glVertexAttrib4fv(void *self , GLuint indx, const GLfloat* values) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(indx);
+    ctx->m_glVertexAttrib4fv_enc(ctx, indx, values);
+}
+
+void GL2Encoder::s_glVertexAttribI4i(void *self , GLuint index, GLint v0, GLint v1, GLint v2, GLint v3) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
+    ctx->m_glVertexAttribI4i_enc(ctx, index, v0, v1, v2, v3);
+}
+
+void GL2Encoder::s_glVertexAttribI4ui(void *self , GLuint index, GLuint v0, GLuint v1, GLuint v2, GLuint v3) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
+    ctx->m_glVertexAttribI4ui_enc(ctx, index, v0, v1, v2, v3);
+}
+
+void GL2Encoder::s_glVertexAttribI4iv(void *self , GLuint index, const GLint* v) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
+    ctx->m_glVertexAttribI4iv_enc(ctx, index, v);
+}
+
+void GL2Encoder::s_glVertexAttribI4uiv(void *self , GLuint index, const GLuint* v) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
+    ctx->m_glVertexAttribI4uiv_enc(ctx, index, v);
+}
+
+void GL2Encoder::s_glGetShaderPrecisionFormat(void *self , GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!GLESv2Validation::allowedShaderType(shadertype), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::allowedPrecisionType(precisiontype), GL_INVALID_ENUM);
+    ctx->m_glGetShaderPrecisionFormat_enc(ctx, shadertype, precisiontype, range, precision);
+}
+
+void GL2Encoder::s_glGetProgramiv(void *self , GLuint program, GLenum pname, GLint* params) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!GLESv2Validation::allowedGetProgram(ctx->majorVersion(), ctx->minorVersion(), pname), GL_INVALID_ENUM);
+    VALIDATE_PROGRAM_NAME(program);
+    ctx->m_glGetProgramiv_enc(ctx, program, pname, params);
+}
+
+void GL2Encoder::s_glGetActiveUniform(void *self , GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_PROGRAM_NAME(program);
+    SET_ERROR_IF(index >= ctx->m_shared->getActiveUniformsCountForProgram(program), GL_INVALID_VALUE);
+    ctx->m_glGetActiveUniform_enc(ctx, program, index, bufsize, length, size, type, name);
+}
+
+void GL2Encoder::s_glGetActiveUniformsiv(void *self , GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_PROGRAM_NAME(program);
+    SET_ERROR_IF(uniformCount < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(!GLESv2Validation::allowedGetActiveUniforms(pname), GL_INVALID_ENUM);
+    int activeUniformsCount = ctx->m_shared->getActiveUniformsCountForProgram(program);
+    for (GLsizei i = 0; i < uniformCount; ++i) {
+        SET_ERROR_IF(uniformIndices[i] >= activeUniformsCount, GL_INVALID_VALUE);
+    }
+    ctx->m_glGetActiveUniformsiv_enc(ctx, program, uniformCount, uniformIndices, pname, params);
+}
+
+void GL2Encoder::s_glGetActiveUniformBlockName(void *self , GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformBlockName) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_PROGRAM_NAME(program);
+    SET_ERROR_IF(bufSize < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(uniformBlockIndex >= ctx->m_shared->getActiveUniformBlockCount(program), GL_INVALID_VALUE);
+    ctx->m_glGetActiveUniformBlockName_enc(ctx, program, uniformBlockIndex, bufSize, length, uniformBlockName);
+}
+
+void GL2Encoder::s_glGetActiveAttrib(void *self , GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_PROGRAM_NAME(program);
+    VALIDATE_VERTEX_ATTRIB_INDEX(index);
+    SET_ERROR_IF(bufsize < 0, GL_INVALID_VALUE);
+    SET_ERROR_IF(index >= ctx->m_shared->getActiveAttributesCountForProgram(program), GL_INVALID_VALUE);
+    ctx->m_glGetActiveAttrib_enc(ctx, program, index, bufsize, length, size, type, name);
+}
+
+void GL2Encoder::s_glGetRenderbufferParameteriv(void *self , GLenum target, GLenum pname, GLint* params) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(target != GL_RENDERBUFFER, GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::allowedGetRenderbufferParameter(pname), GL_INVALID_ENUM);
+    ctx->m_glGetRenderbufferParameteriv_enc(ctx, target, pname, params);
+}
+
+void GL2Encoder::s_glGetQueryiv(void *self , GLenum target, GLenum pname, GLint* params) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!GLESv2Validation::allowedQueryTarget(target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::allowedQueryParam(pname), GL_INVALID_ENUM);
+    ctx->m_glGetQueryiv_enc(ctx, target, pname, params);
+}
+
+void GL2Encoder::s_glGetQueryObjectuiv(void *self , GLuint query, GLenum pname, GLuint* params) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    GLClientState* state = ctx->m_state;
+    SET_ERROR_IF(!GLESv2Validation::allowedQueryObjectParam(pname), GL_INVALID_ENUM);
+    SET_ERROR_IF(!state->queryExistence(GLClientState::ObjectType::Query, query), GL_INVALID_OPERATION);
+    SET_ERROR_IF(!state->getLastQueryTarget(query), GL_INVALID_OPERATION);
+    SET_ERROR_IF(ctx->m_state->isQueryObjectActive(query), GL_INVALID_OPERATION);
+
+    ctx->m_glGetQueryObjectuiv_enc(ctx, query, pname, params);
+}
+
+GLboolean GL2Encoder::s_glIsEnabled(void *self , GLenum cap) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+	RET_AND_SET_ERROR_IF(!GLESv2Validation::allowedEnable(ctx->majorVersion(), ctx->minorVersion(), cap), GL_INVALID_ENUM, 0);
+    return ctx->m_glIsEnabled_enc(ctx, cap);
+}
+
+void GL2Encoder::s_glHint(void *self , GLenum target, GLenum mode) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    SET_ERROR_IF(!GLESv2Validation::allowedHintTarget(target), GL_INVALID_ENUM);
+    SET_ERROR_IF(!GLESv2Validation::allowedHintMode(mode), GL_INVALID_ENUM);
+    ctx->m_glHint_enc(ctx, target, mode);
+}
+
+GLint GL2Encoder::s_glGetFragDataLocation (void *self , GLuint program, const char* name) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    VALIDATE_PROGRAM_NAME_RET(program, -1);
+    RET_AND_SET_ERROR_IF(!ctx->m_shared->getProgramLinkStatus(program), GL_INVALID_OPERATION, -1);
+    return ctx->m_glGetFragDataLocation_enc(ctx, program, name);
+}
diff --git a/system/GLESv2_enc/GL2Encoder.h b/system/GLESv2_enc/GL2Encoder.h
index 0b42cb7..61aed34 100644
--- a/system/GLESv2_enc/GL2Encoder.h
+++ b/system/GLESv2_enc/GL2Encoder.h
@@ -62,8 +62,11 @@
     }
     void setSharedGroup(GLSharedGroupPtr shared) {
         m_shared = shared;
-        if (m_state && m_shared.Ptr())
+        if (m_state && m_shared.Ptr()) {
             m_state->setTextureData(m_shared->getTextureData());
+            m_state->setRenderbufferInfo(m_shared->getRenderbufferInfo());
+            m_state->setSamplerInfo(m_shared->getSamplerInfo());
+        }
     }
     int majorVersion() const { return m_currMajorVersion; }
     int minorVersion() const { return m_currMinorVersion; }
@@ -71,6 +74,7 @@
                        const std::vector<std::string>& extArray) {
         m_currExtensions = std::string(exts);
         m_currExtensionsArray = extArray;
+        m_state->setExtensions(m_currExtensions);
     }
     bool hasExtension(const char* ext) const {
         return m_currExtensions.find(ext) != std::string::npos;
@@ -85,6 +89,9 @@
     virtual void setError(GLenum error){ m_error = error; };
     virtual GLenum getError() { return m_error; };
 
+    __attribute__((always_inline)) GLenum* getErrorPtr() { return &m_error; }
+    __attribute__((always_inline)) bool hasError() const { return m_error != GL_NO_ERROR; }
+
     void override2DTextureTarget(GLenum target);
     void restore2DTextureTarget(GLenum target);
     void associateEGLImage(GLenum target, GLeglImageOES eglImage);
@@ -118,6 +125,7 @@
 
     GLint m_max_combinedTextureImageUnits;
     GLint m_max_vertexTextureImageUnits;
+    GLint m_max_array_texture_layers;;
     GLint m_max_textureImageUnits;
     GLint m_max_cubeMapTextureSize;
     GLint m_max_renderBufferSize;
@@ -137,6 +145,8 @@
     GLuint m_ssbo_offset_align;
     GLuint m_ubo_offset_align;
 
+    GLint m_log2MaxTextureSize;
+
     std::vector<char> m_fixedBuffer;
 
     uint32_t m_drawCallFlushInterval;
@@ -161,8 +171,6 @@
     bool updateHostTexture2DBinding(GLenum texUnit, GLenum newTarget);
     void updateHostTexture2DBindingsFromProgramData(GLuint program);
     bool texture2DNeedsOverride(GLenum target) const;
-    bool isCompleteFbo(GLenum target, const GLClientState* state, GLenum attachment) const;
-    bool checkFramebufferCompleteness(GLenum target, const GLClientState* state) const;
 
     // Utility classes for safe queries that
     // need access to private class members
@@ -614,7 +622,6 @@
 
     glBindSampler_client_proc_t m_glBindSampler_enc;
     static void s_glBindSampler(void* self, GLuint unit, GLuint sampler);
-    void doSamplerBindEncodeCached(GLuint unit, GLuint sampler);
 
     glDeleteSamplers_client_proc_t m_glDeleteSamplers_enc;
     static void s_glDeleteSamplers(void* self, GLsizei n, const GLuint* samplers);
@@ -776,6 +783,160 @@
     glDispatchCompute_client_proc_t m_glDispatchCompute_enc;
     glDispatchComputeIndirect_client_proc_t m_glDispatchComputeIndirect_enc;
 
+    // State tracking for transform feedbacks, samplers, and query objects
+    static void s_glGenTransformFeedbacks(void* self, GLsizei n, GLuint* ids);
+    static void s_glDeleteTransformFeedbacks(void* self, GLsizei n, const GLuint* ids);
+    static void s_glGenSamplers(void* self, GLsizei n, GLuint* ids);
+    static void s_glGenQueries(void* self, GLsizei n, GLuint* ids);
+    static void s_glDeleteQueries(void* self, GLsizei n, const GLuint* ids);
+
+    glGenTransformFeedbacks_client_proc_t m_glGenTransformFeedbacks_enc;
+    glDeleteTransformFeedbacks_client_proc_t m_glDeleteTransformFeedbacks_enc;
+    glGenSamplers_client_proc_t m_glGenSamplers_enc;
+    glGenQueries_client_proc_t m_glGenQueries_enc;
+    glDeleteQueries_client_proc_t m_glDeleteQueries_enc;
+
+    static void s_glBindTransformFeedback(void* self, GLenum target, GLuint id);
+    static void s_glBeginQuery(void* self, GLenum target, GLuint query);
+    static void s_glEndQuery(void* self, GLenum target);
+
+    glBindTransformFeedback_client_proc_t m_glBindTransformFeedback_enc;
+    glBeginQuery_client_proc_t m_glBeginQuery_enc;
+    glEndQuery_client_proc_t m_glEndQuery_enc;
+
+    static void s_glClear(void* self, GLbitfield mask);
+    glClear_client_proc_t m_glClear_enc;
+
+    static void s_glClearBufferfi(void* self, GLenum buffer, GLint drawBuffer, float depth, int stencil);
+    glClearBufferfi_client_proc_t m_glClearBufferfi_enc;
+
+    static void s_glCopyTexSubImage2D(void *self , GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+    glCopyTexSubImage2D_client_proc_t m_glCopyTexSubImage2D_enc;
+
+    static void s_glCopyTexSubImage3D(void *self , GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+    glCopyTexSubImage3D_client_proc_t m_glCopyTexSubImage3D_enc;
+
+    static void s_glCompileShader(void* self, GLuint shader);
+    glCompileShader_client_proc_t m_glCompileShader_enc;
+
+    static void s_glValidateProgram(void* self, GLuint program);
+    glValidateProgram_client_proc_t m_glValidateProgram_enc;
+
+    static void s_glProgramBinary(void *self , GLuint program, GLenum binaryFormat, const void* binary, GLsizei length);
+    glProgramBinary_client_proc_t m_glProgramBinary_enc;
+
+    static void s_glGetSamplerParameterfv(void *self, GLuint sampler, GLenum pname, GLfloat* params);
+    static void s_glGetSamplerParameteriv(void *self, GLuint sampler, GLenum pname, GLint* params);
+    glGetSamplerParameterfv_client_proc_t m_glGetSamplerParameterfv_enc;
+    glGetSamplerParameteriv_client_proc_t m_glGetSamplerParameteriv_enc;
+
+    static void s_glSamplerParameterf(void *self , GLuint sampler, GLenum pname, GLfloat param);
+    static void s_glSamplerParameteri(void *self , GLuint sampler, GLenum pname, GLint param);
+    static void s_glSamplerParameterfv(void *self , GLuint sampler, GLenum pname, const GLfloat* params);
+    static void s_glSamplerParameteriv(void *self , GLuint sampler, GLenum pname, const GLint* params);
+
+    glSamplerParameterf_client_proc_t m_glSamplerParameterf_enc;
+    glSamplerParameteri_client_proc_t m_glSamplerParameteri_enc;
+    glSamplerParameterfv_client_proc_t m_glSamplerParameterfv_enc;
+    glSamplerParameteriv_client_proc_t m_glSamplerParameteriv_enc;
+
+    static int s_glGetAttribLocation(void *self , GLuint program, const GLchar* name);
+    glGetAttribLocation_client_proc_t m_glGetAttribLocation_enc;
+
+    static void s_glBindAttribLocation(void *self , GLuint program, GLuint index, const GLchar* name);
+    static void s_glUniformBlockBinding(void *self , GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
+    static void s_glGetTransformFeedbackVarying(void *self , GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLsizei* size, GLenum* type, char* name);
+    static void s_glScissor(void *self , GLint x, GLint y, GLsizei width, GLsizei height);
+    static void s_glDepthFunc(void *self , GLenum func);
+    static void s_glViewport(void *self , GLint x, GLint y, GLsizei width, GLsizei height);
+    static void s_glStencilFunc(void *self , GLenum func, GLint ref, GLuint mask);
+    static void s_glStencilFuncSeparate(void *self , GLenum face, GLenum func, GLint ref, GLuint mask);
+    static void s_glStencilOp(void *self , GLenum fail, GLenum zfail, GLenum zpass);
+    static void s_glStencilOpSeparate(void *self , GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
+    static void s_glStencilMaskSeparate(void *self , GLenum face, GLuint mask);
+    static void s_glBlendEquation(void *self , GLenum mode);
+    static void s_glBlendEquationSeparate(void *self , GLenum modeRGB, GLenum modeAlpha);
+    static void s_glBlendFunc(void *self , GLenum sfactor, GLenum dfactor);
+    static void s_glBlendFuncSeparate(void *self , GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
+    static void s_glCullFace(void *self , GLenum mode);
+    static void s_glFrontFace(void *self , GLenum mode);
+    static void s_glLineWidth(void *self , GLfloat width);
+    static void s_glVertexAttrib1f(void *self , GLuint indx, GLfloat x);
+    static void s_glVertexAttrib2f(void *self , GLuint indx, GLfloat x, GLfloat y);
+    static void s_glVertexAttrib3f(void *self , GLuint indx, GLfloat x, GLfloat y, GLfloat z);
+    static void s_glVertexAttrib4f(void *self , GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+    static void s_glVertexAttrib1fv(void *self , GLuint indx, const GLfloat* values);
+    static void s_glVertexAttrib2fv(void *self , GLuint indx, const GLfloat* values);
+    static void s_glVertexAttrib3fv(void *self , GLuint indx, const GLfloat* values);
+    static void s_glVertexAttrib4fv(void *self , GLuint indx, const GLfloat* values);
+    static void s_glVertexAttribI4i(void *self , GLuint index, GLint v0, GLint v1, GLint v2, GLint v3);
+    static void s_glVertexAttribI4ui(void *self , GLuint index, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
+    static void s_glVertexAttribI4iv(void *self , GLuint index, const GLint* v);
+    static void s_glVertexAttribI4uiv(void *self , GLuint index, const GLuint* v);
+
+    static void s_glGetShaderPrecisionFormat(void *self , GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision);
+    static void s_glGetProgramiv(void *self , GLuint program, GLenum pname, GLint* params);
+    static void s_glGetActiveUniform(void *self , GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
+    static void s_glGetActiveUniformsiv(void *self , GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params);
+    static void s_glGetActiveUniformBlockName(void *self , GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformBlockName);
+    static void s_glGetActiveAttrib(void *self , GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
+    static void s_glGetRenderbufferParameteriv(void *self , GLenum target, GLenum pname, GLint* params);
+    static void s_glGetQueryiv(void *self , GLenum target, GLenum pname, GLint* params);
+    static void s_glGetQueryObjectuiv(void *self , GLuint query, GLenum pname, GLuint* params);
+    static GLboolean s_glIsEnabled(void *self , GLenum cap);
+    static void s_glHint(void *self , GLenum target, GLenum mode);
+    static GLint s_glGetFragDataLocation (void *self , GLuint program, const char* name);
+
+#define LIST_REMAINING_FUNCTIONS_FOR_VALIDATION(f) \
+    f(glBindAttribLocation) \
+    f(glUniformBlockBinding) \
+    f(glGetTransformFeedbackVarying) \
+    f(glScissor) \
+    f(glDepthFunc) \
+    f(glViewport) \
+    f(glStencilFunc) \
+    f(glStencilFuncSeparate) \
+    f(glStencilOp) \
+    f(glStencilOpSeparate) \
+    f(glStencilMaskSeparate) \
+    f(glBlendEquation) \
+    f(glBlendEquationSeparate) \
+    f(glBlendFunc) \
+    f(glBlendFuncSeparate) \
+    f(glCullFace) \
+    f(glFrontFace) \
+    f(glLineWidth) \
+    f(glVertexAttrib1f) \
+    f(glVertexAttrib2f) \
+    f(glVertexAttrib3f) \
+    f(glVertexAttrib4f) \
+    f(glVertexAttrib1fv) \
+    f(glVertexAttrib2fv) \
+    f(glVertexAttrib3fv) \
+    f(glVertexAttrib4fv) \
+    f(glVertexAttribI4i) \
+    f(glVertexAttribI4ui) \
+    f(glVertexAttribI4iv) \
+    f(glVertexAttribI4uiv) \
+    f(glGetShaderPrecisionFormat) \
+    f(glGetProgramiv) \
+    f(glGetActiveUniform) \
+    f(glGetActiveUniformsiv) \
+    f(glGetActiveUniformBlockName) \
+    f(glGetActiveAttrib) \
+    f(glGetRenderbufferParameteriv) \
+    f(glGetQueryiv) \
+    f(glGetQueryObjectuiv) \
+    f(glIsEnabled) \
+    f(glHint) \
+    f(glGetFragDataLocation) \
+
+#define DECLARE_CLIENT_ENCODER_PROC(n) \
+    n##_client_proc_t m_##n##_enc;
+
+    LIST_REMAINING_FUNCTIONS_FOR_VALIDATION(DECLARE_CLIENT_ENCODER_PROC)
+
+
 public:
     glEGLImageTargetTexture2DOES_client_proc_t m_glEGLImageTargetTexture2DOES_enc;
 
diff --git a/system/GLESv2_enc/GLESv2Validation.cpp b/system/GLESv2_enc/GLESv2Validation.cpp
index aff7902..b3b4637 100644
--- a/system/GLESv2_enc/GLESv2Validation.cpp
+++ b/system/GLESv2_enc/GLESv2Validation.cpp
@@ -18,6 +18,241 @@
 
 #include <sstream>
 
+#define LIST_VALID_TEX_INTERNALFORMATS(f) \
+    f(GL_BGRA8_EXT) \
+    f(GL_R8) \
+    f(GL_R8_SNORM) \
+    f(GL_R16F) \
+    f(GL_R32F) \
+    f(GL_R8UI) \
+    f(GL_R8I) \
+    f(GL_R16UI) \
+    f(GL_R16I) \
+    f(GL_R32UI) \
+    f(GL_R32I) \
+    f(GL_RG8) \
+    f(GL_RG8_SNORM) \
+    f(GL_RG16F) \
+    f(GL_RG32F) \
+    f(GL_RG8UI) \
+    f(GL_RG8I) \
+    f(GL_RG16UI) \
+    f(GL_RG16I) \
+    f(GL_RG32UI) \
+    f(GL_RG32I) \
+    f(GL_RGB8) \
+    f(GL_SRGB8) \
+    f(GL_RGB565) \
+    f(GL_RGB8_SNORM) \
+    f(GL_R11F_G11F_B10F) \
+    f(GL_RGB9_E5) \
+    f(GL_RGB16F) \
+    f(GL_RGB32F) \
+    f(GL_RGB8UI) \
+    f(GL_RGB8I) \
+    f(GL_RGB16UI) \
+    f(GL_RGB16I) \
+    f(GL_RGB32UI) \
+    f(GL_RGB32I) \
+    f(GL_RGBA8) \
+    f(GL_SRGB8_ALPHA8) \
+    f(GL_RGBA8_SNORM) \
+    f(GL_RGB5_A1) \
+    f(GL_RGBA4) \
+    f(GL_RGB10_A2) \
+    f(GL_RGBA16F) \
+    f(GL_RGBA32F) \
+    f(GL_RGBA8UI) \
+    f(GL_RGBA8I) \
+    f(GL_RGB10_A2UI) \
+    f(GL_RGBA16UI) \
+    f(GL_RGBA16I) \
+    f(GL_RGBA32I) \
+    f(GL_RGBA32UI) \
+    f(GL_DEPTH_COMPONENT16) \
+    f(GL_DEPTH_COMPONENT24) \
+    f(GL_DEPTH_COMPONENT32F) \
+    f(GL_DEPTH24_STENCIL8) \
+    f(GL_DEPTH32F_STENCIL8) \
+    f(GL_ETC1_RGB8_OES) \
+    f(GL_COMPRESSED_R11_EAC) \
+    f(GL_COMPRESSED_SIGNED_R11_EAC) \
+    f(GL_COMPRESSED_RG11_EAC) \
+    f(GL_COMPRESSED_SIGNED_RG11_EAC) \
+    f(GL_COMPRESSED_RGB8_ETC2) \
+    f(GL_COMPRESSED_SRGB8_ETC2) \
+    f(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) \
+    f(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) \
+    f(GL_COMPRESSED_RGBA8_ETC2_EAC) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) \
+    f(GL_COMPRESSED_RGBA_ASTC_4x4_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_5x4_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_5x5_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_6x5_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_6x6_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_8x5_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_8x6_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_8x8_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_10x5_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_10x6_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_10x8_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_10x10_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_12x10_KHR) \
+    f(GL_COMPRESSED_RGBA_ASTC_12x12_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR) \
+
+#define LIST_INTEGER_TEX_FORMATS(f) \
+    f(GL_RED_INTEGER) \
+    f(GL_RG_INTEGER) \
+    f(GL_RGB_INTEGER) \
+    f(GL_RGBA_INTEGER) \
+    f(GL_R8UI) \
+    f(GL_R8I) \
+    f(GL_R16UI) \
+    f(GL_R16I) \
+    f(GL_R32UI) \
+    f(GL_R32I) \
+    f(GL_RG8UI) \
+    f(GL_RG8I) \
+    f(GL_RG16UI) \
+    f(GL_RG16I) \
+    f(GL_RG32UI) \
+    f(GL_RG32I) \
+    f(GL_RGB8UI) \
+    f(GL_RGB8I) \
+    f(GL_RGB16UI) \
+    f(GL_RGB16I) \
+    f(GL_RGB32UI) \
+    f(GL_RGB32I) \
+    f(GL_RGBA8UI) \
+    f(GL_RGBA8I) \
+    f(GL_RGB10_A2UI) \
+    f(GL_RGBA16UI) \
+    f(GL_RGBA16I) \
+    f(GL_RGBA32I) \
+    f(GL_RGBA32UI) \
+
+#define LIST_VALID_TEXFORMAT_COMBINATIONS(f) \
+    f(GL_BGRA8_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE) \
+    f(GL_R8, GL_RED, GL_UNSIGNED_BYTE) \
+    f(GL_R8_SNORM, GL_RED, GL_BYTE) \
+    f(GL_R16F, GL_RED, GL_FLOAT) \
+    f(GL_R16F, GL_RED, GL_HALF_FLOAT) \
+    f(GL_R32F, GL_RED, GL_FLOAT) \
+    f(GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE) \
+    f(GL_R8I, GL_RED_INTEGER, GL_BYTE) \
+    f(GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT) \
+    f(GL_R16I, GL_RED_INTEGER, GL_SHORT) \
+    f(GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT) \
+    f(GL_R32I, GL_RED_INTEGER, GL_INT) \
+    f(GL_RG8, GL_RG, GL_UNSIGNED_BYTE) \
+    f(GL_RG8_SNORM, GL_RG, GL_BYTE) \
+    f(GL_RG16F, GL_RG, GL_HALF_FLOAT) \
+    f(GL_RG16F, GL_RG, GL_FLOAT) \
+    f(GL_RG32F, GL_RG, GL_FLOAT) \
+    f(GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE) \
+    f(GL_RG8I, GL_RG_INTEGER, GL_BYTE) \
+    f(GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT) \
+    f(GL_RG16I, GL_RG_INTEGER, GL_SHORT) \
+    f(GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT) \
+    f(GL_RG32I, GL_RG_INTEGER, GL_INT) \
+    f(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE) \
+    f(GL_SRGB8, GL_RGB, GL_UNSIGNED_BYTE) \
+    f(GL_RGB565, GL_RGB, GL_UNSIGNED_BYTE) \
+    f(GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5) \
+    f(GL_RGB8_SNORM, GL_RGB, GL_BYTE) \
+    f(GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV) \
+    f(GL_R11F_G11F_B10F, GL_RGB, GL_HALF_FLOAT) \
+    f(GL_R11F_G11F_B10F, GL_RGB, GL_FLOAT) \
+    f(GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV) \
+    f(GL_RGB9_E5, GL_RGB, GL_HALF_FLOAT) \
+    f(GL_RGB9_E5, GL_RGB, GL_FLOAT) \
+    f(GL_RGB16F, GL_RGB, GL_HALF_FLOAT) \
+    f(GL_RGB16F, GL_RGB, GL_FLOAT) \
+    f(GL_RGB32F, GL_RGB, GL_FLOAT) \
+    f(GL_RGB8UI, GL_RGB_INTEGER, GL_UNSIGNED_BYTE) \
+    f(GL_RGB8I, GL_RGB_INTEGER, GL_BYTE) \
+    f(GL_RGB16UI, GL_RGB_INTEGER, GL_UNSIGNED_SHORT) \
+    f(GL_RGB16I, GL_RGB_INTEGER, GL_SHORT) \
+    f(GL_RGB32UI, GL_RGB_INTEGER, GL_UNSIGNED_INT) \
+    f(GL_RGB32I, GL_RGB_INTEGER, GL_INT) \
+    f(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_RGBA8_SNORM, GL_RGBA, GL_BYTE) \
+    f(GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1) \
+    f(GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV) \
+    f(GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4) \
+    f(GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV) \
+    f(GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT) \
+    f(GL_RGBA16F, GL_RGBA, GL_FLOAT) \
+    f(GL_RGBA32F, GL_RGBA, GL_FLOAT) \
+    f(GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE) \
+    f(GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE) \
+    f(GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV) \
+    f(GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT) \
+    f(GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT) \
+    f(GL_RGBA32I, GL_RGBA_INTEGER, GL_INT) \
+    f(GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT) \
+    f(GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT) \
+    f(GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT) \
+    f(GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT) \
+    f(GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT) \
+    f(GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8) \
+    f(GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV) \
+    f(GL_COMPRESSED_R11_EAC, GL_RED, GL_FLOAT) \
+    f(GL_COMPRESSED_SIGNED_R11_EAC, GL_RED, GL_FLOAT) \
+    f(GL_COMPRESSED_RG11_EAC, GL_RG, GL_FLOAT) \
+    f(GL_COMPRESSED_SIGNED_RG11_EAC, GL_RG, GL_FLOAT) \
+    f(GL_COMPRESSED_RGB8_ETC2, GL_RGB, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ETC2, GL_RGB, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA8_ETC2_EAC, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_5x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_6x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_8x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_8x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_10x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_10x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_10x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_12x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \
+
 namespace GLESv2Validation {
 
 GLbitfield allBufferMapAccessFlags =
@@ -69,6 +304,24 @@
     }
 }
 
+bool bufferUsage(GL2Encoder* ctx, GLenum usage) {
+    int glesMajorVersion = ctx->majorVersion();
+    switch(usage) {
+        case GL_STREAM_DRAW:
+        case GL_STATIC_DRAW:
+        case GL_DYNAMIC_DRAW:
+            return true;
+        case GL_STREAM_READ:
+        case GL_STATIC_READ:
+        case GL_DYNAMIC_READ:
+        case GL_STREAM_COPY:
+        case GL_STATIC_COPY:
+        case GL_DYNAMIC_COPY:
+            return glesMajorVersion >= 3;
+    }
+    return false;
+
+}
 bool pixelStoreParam(GL2Encoder* ctx, GLenum param) {
     int glesMajorVersion = ctx->majorVersion();
     switch(param) {
@@ -260,6 +513,17 @@
     return false;
 }
 
+bool pixelOp(GLenum format,GLenum type) {
+     switch(type) {
+     case GL_UNSIGNED_SHORT_4_4_4_4:
+     case GL_UNSIGNED_SHORT_5_5_5_1:
+         return format == GL_RGBA;
+     case GL_UNSIGNED_SHORT_5_6_5:
+         return format == GL_RGB;
+     }
+     return true;
+}
+
 bool vertexAttribType(GL2Encoder* ctx, GLenum type)
 {
     int glesMajorVersion = ctx->majorVersion();
@@ -347,32 +611,133 @@
     return false;
 }
 
-static GLsizei ceildiv(GLsizei x, GLsizei y) {
-    return (x + y - 1) / y;
+bool textureParams(GL2Encoder* ctx, GLenum param) {
+    int glesMajorVersion = ctx->majorVersion();
+    int glesMinorVersion = ctx->minorVersion();
+    switch(param) {
+    case GL_TEXTURE_MIN_FILTER:
+    case GL_TEXTURE_MAG_FILTER:
+    case GL_TEXTURE_WRAP_S:
+    case GL_TEXTURE_WRAP_T:
+    case GL_TEXTURE_MAX_ANISOTROPY_EXT:
+        return true;
+    case GL_TEXTURE_SWIZZLE_R:
+    case GL_TEXTURE_SWIZZLE_G:
+    case GL_TEXTURE_SWIZZLE_B:
+    case GL_TEXTURE_SWIZZLE_A:
+    case GL_TEXTURE_MIN_LOD:
+    case GL_TEXTURE_MAX_LOD:
+    case GL_TEXTURE_BASE_LEVEL:
+    case GL_TEXTURE_MAX_LEVEL:
+    case GL_TEXTURE_COMPARE_MODE:
+    case GL_TEXTURE_COMPARE_FUNC:
+    case GL_TEXTURE_WRAP_R:
+    case GL_TEXTURE_IMMUTABLE_FORMAT:
+    case GL_TEXTURE_IMMUTABLE_LEVELS:
+        return glesMajorVersion >= 3;
+    case GL_DEPTH_STENCIL_TEXTURE_MODE:
+        return glesMajorVersion >= 3 && glesMinorVersion >= 1;
+    default:
+        return false;
+    }
 }
 
-GLsizei compressedTexImageSize(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) {
-    GLsizei base_size = ceildiv(width, 4) * ceildiv(height, 4) * depth;
-#define COMPRESSED_TEX_IMAGE_SIZE_CASE(internal, multiplier) \
-    case internal: \
-        return base_size * multiplier; \
-
-    switch (internalformat) {
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_R11_EAC, 8)
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_SIGNED_R11_EAC, 8)
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_RG11_EAC, 16)
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_SIGNED_RG11_EAC, 16)
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_RGB8_ETC2, 8)
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_SRGB8_ETC2, 8)
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, 8)
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, 8)
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_RGBA8_ETC2_EAC, 16)
-    COMPRESSED_TEX_IMAGE_SIZE_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, 16)
-    default:
-        break;
+bool samplerParams(GL2Encoder* ctx, GLenum param) {
+    (void)ctx;
+    switch(param) {
+        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
+        case GL_TEXTURE_MIN_FILTER:
+        case GL_TEXTURE_MAG_FILTER:
+        case GL_TEXTURE_WRAP_S:
+        case GL_TEXTURE_WRAP_T:
+        case GL_TEXTURE_WRAP_R:
+        case GL_TEXTURE_MIN_LOD:
+        case GL_TEXTURE_MAX_LOD:
+        case GL_TEXTURE_COMPARE_MODE:
+        case GL_TEXTURE_COMPARE_FUNC:
+            return true;
+        default:
+            return false;
     }
+}
 
-    return 0;
+bool textureParamValue(GL2Encoder* ctx, GLenum pname, GLint intval, GLfloat floatval, GLenum enumval) {
+    (void)ctx;
+    (void)floatval;
+    switch (pname) {
+    case GL_TEXTURE_BASE_LEVEL:
+        return intval >= 0;
+    case GL_TEXTURE_COMPARE_MODE:
+        return
+            (enumval == GL_NONE) ||
+            (enumval == GL_COMPARE_REF_TO_TEXTURE);
+    case GL_TEXTURE_COMPARE_FUNC:
+        return
+            (enumval == GL_LEQUAL) ||
+            (enumval == GL_GEQUAL) ||
+            (enumval == GL_LESS) ||
+            (enumval == GL_GREATER) ||
+            (enumval == GL_EQUAL) ||
+            (enumval == GL_NOTEQUAL) ||
+            (enumval == GL_ALWAYS) ||
+            (enumval == GL_NEVER);
+    case GL_TEXTURE_MAG_FILTER:
+        return
+            (enumval == GL_NEAREST) ||
+            (enumval == GL_LINEAR);
+    case GL_TEXTURE_MAX_LEVEL:
+        return intval >= 0;
+    case GL_TEXTURE_MAX_LOD:
+        return true;
+    case GL_TEXTURE_MIN_FILTER:
+        return
+            (enumval == GL_NEAREST) ||
+            (enumval == GL_LINEAR) ||
+            (enumval == GL_NEAREST_MIPMAP_NEAREST) ||
+            (enumval == GL_NEAREST_MIPMAP_LINEAR) ||
+            (enumval == GL_LINEAR_MIPMAP_NEAREST) ||
+            (enumval == GL_LINEAR_MIPMAP_LINEAR);
+    case GL_TEXTURE_MIN_LOD:
+        return true;
+    case GL_TEXTURE_SWIZZLE_R:
+    case GL_TEXTURE_SWIZZLE_G:
+    case GL_TEXTURE_SWIZZLE_B:
+    case GL_TEXTURE_SWIZZLE_A:
+        return
+            (enumval == GL_RED) ||
+            (enumval == GL_GREEN) ||
+            (enumval == GL_BLUE) ||
+            (enumval == GL_ALPHA) ||
+            (enumval == GL_ZERO) ||
+            (enumval == GL_ONE);
+    case GL_TEXTURE_WRAP_S:
+    case GL_TEXTURE_WRAP_T:
+    case GL_TEXTURE_WRAP_R:
+        return
+            (enumval == GL_CLAMP_TO_EDGE) ||
+            (enumval == GL_REPEAT) ||
+            (enumval == GL_MIRRORED_REPEAT);
+    case GL_TEXTURE_MAX_ANISOTROPY_EXT:
+        return true;
+    case GL_TEXTURE_IMMUTABLE_FORMAT:
+    case GL_TEXTURE_IMMUTABLE_LEVELS:
+    case GL_DEPTH_STENCIL_TEXTURE_MODE:
+        return true;
+    default:
+        return true;
+    }
+}
+
+bool isIntegerFormat(GLenum format) {
+
+#define CHECK_EQUAL(x) case x: return true;
+
+    switch (format) {
+        LIST_INTEGER_TEX_FORMATS(CHECK_EQUAL)
+
+    default:
+        return false;
+    }
 }
 
 bool isCompressedFormat(GLenum internalformat) {
@@ -445,6 +810,7 @@
         return false ; \
 
     switch (internalformat) {
+    COMPRESSED_TEX_IMAGE_SUPPORT_CASE(GL_ETC1_RGB8_OES, 2, 0)
     COMPRESSED_TEX_IMAGE_SUPPORT_CASE(GL_COMPRESSED_R11_EAC, 2, 0)
     COMPRESSED_TEX_IMAGE_SUPPORT_CASE(GL_COMPRESSED_SIGNED_R11_EAC, 2, 0)
     COMPRESSED_TEX_IMAGE_SUPPORT_CASE(GL_COMPRESSED_RG11_EAC, 2, 0)
@@ -486,7 +852,7 @@
     default:
         break;
     }
-    return true;
+    return false;
 }
 
 bool unsizedFormat(GLenum format) {
@@ -730,103 +1096,6 @@
     }
     return false;
 }
-#define LIST_VALID_TEX_INTERNALFORMATS(f) \
-    f(GL_BGRA8_EXT) \
-    f(GL_R8) \
-    f(GL_R8_SNORM) \
-    f(GL_R16F) \
-    f(GL_R32F) \
-    f(GL_R8UI) \
-    f(GL_R8I) \
-    f(GL_R16UI) \
-    f(GL_R16I) \
-    f(GL_R32UI) \
-    f(GL_R32I) \
-    f(GL_RG8) \
-    f(GL_RG8_SNORM) \
-    f(GL_RG16F) \
-    f(GL_RG32F) \
-    f(GL_RG8UI) \
-    f(GL_RG8I) \
-    f(GL_RG16UI) \
-    f(GL_RG16I) \
-    f(GL_RG32UI) \
-    f(GL_RG32I) \
-    f(GL_RGB8) \
-    f(GL_SRGB8) \
-    f(GL_RGB565) \
-    f(GL_RGB8_SNORM) \
-    f(GL_R11F_G11F_B10F) \
-    f(GL_RGB9_E5) \
-    f(GL_RGB16F) \
-    f(GL_RGB32F) \
-    f(GL_RGB8UI) \
-    f(GL_RGB8I) \
-    f(GL_RGB16UI) \
-    f(GL_RGB16I) \
-    f(GL_RGB32UI) \
-    f(GL_RGB32I) \
-    f(GL_RGBA8) \
-    f(GL_SRGB8_ALPHA8) \
-    f(GL_RGBA8_SNORM) \
-    f(GL_RGB5_A1) \
-    f(GL_RGBA4) \
-    f(GL_RGB10_A2) \
-    f(GL_RGBA16F) \
-    f(GL_RGBA32F) \
-    f(GL_RGBA8UI) \
-    f(GL_RGBA8I) \
-    f(GL_RGB10_A2UI) \
-    f(GL_RGBA16UI) \
-    f(GL_RGBA16I) \
-    f(GL_RGBA32I) \
-    f(GL_RGBA32UI) \
-    f(GL_DEPTH_COMPONENT16) \
-    f(GL_DEPTH_COMPONENT24) \
-    f(GL_DEPTH_COMPONENT32F) \
-    f(GL_DEPTH24_STENCIL8) \
-    f(GL_DEPTH32F_STENCIL8) \
-    f(GL_ETC1_RGB8_OES) \
-    f(GL_COMPRESSED_R11_EAC) \
-    f(GL_COMPRESSED_SIGNED_R11_EAC) \
-    f(GL_COMPRESSED_RG11_EAC) \
-    f(GL_COMPRESSED_SIGNED_RG11_EAC) \
-    f(GL_COMPRESSED_RGB8_ETC2) \
-    f(GL_COMPRESSED_SRGB8_ETC2) \
-    f(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) \
-    f(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) \
-    f(GL_COMPRESSED_RGBA8_ETC2_EAC) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) \
-    f(GL_COMPRESSED_RGBA_ASTC_4x4_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_5x4_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_5x5_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_6x5_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_6x6_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_8x5_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_8x6_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_8x8_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_10x5_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_10x6_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_10x8_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_10x10_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_12x10_KHR) \
-    f(GL_COMPRESSED_RGBA_ASTC_12x12_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR) \
-    f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR) \
-
-
 bool pixelInternalFormat(GLenum internalformat) {
 #define VALID_INTERNAL_FORMAT(format) \
     case format: \
@@ -840,6 +1109,83 @@
     return false;
 }
 
+bool pixelSizedFormat(GL2Encoder* ctx, GLenum internalformat, GLenum format, GLenum type) {
+    int glesMajorVersion = ctx->majorVersion();
+    if (internalformat == format) {
+        return true;
+    }
+
+    if (glesMajorVersion < 3) {
+        switch (format) {
+            case GL_RED:
+                switch (type) {
+                    case GL_UNSIGNED_BYTE:
+                        return internalformat == GL_R8;
+                    case GL_HALF_FLOAT:
+                    case GL_FLOAT:
+                        return internalformat == GL_R16F;
+                    case GL_BYTE:
+                        return internalformat == GL_R8_SNORM;
+                    default:
+                        return false;
+                }
+                break;
+            case GL_RG:
+                switch (type) {
+                    case GL_UNSIGNED_BYTE:
+                        return internalformat == GL_RG8;
+                    case GL_HALF_FLOAT:
+                    case GL_FLOAT:
+                        return internalformat == GL_RG16F;
+                    default:
+                        return false;
+                }
+                break;
+            case GL_RGB:
+                switch (type) {
+                    case GL_HALF_FLOAT:
+                    case GL_FLOAT:
+                        return internalformat == GL_RGB16F
+                            || internalformat == GL_R11F_G11F_B10F;
+                    case GL_UNSIGNED_INT_10F_11F_11F_REV:
+                        return internalformat == GL_R11F_G11F_B10F;
+                    default:
+                        return internalformat == GL_RGB8 ||
+                               internalformat == GL_RGB;
+                }
+                break;
+            case GL_RGBA:
+                switch (type) {
+                    case GL_HALF_FLOAT:
+                    case GL_FLOAT:
+                        return internalformat == GL_RGBA16F;
+                    default:
+                        return internalformat == GL_RGBA8 ||
+                               internalformat == GL_RGBA;
+                }
+                break;
+        }
+    }
+
+#define VALIDATE_FORMAT_COMBINATION(x, y, z) \
+    if (internalformat == x && format == y && type == z) return true; \
+
+    LIST_VALID_TEXFORMAT_COMBINATIONS(VALIDATE_FORMAT_COMBINATION)
+
+    return false;
+}
+
+void getCompatibleFormatTypeForInternalFormat(GLenum internalformat, GLenum* format_out, GLenum* type_out) {
+#define RETURN_COMPATIBLE_FORMAT(x, y, z) \
+    if (internalformat == x) { \
+        *format_out = y; \
+        *type_out = z; \
+        return; \
+    } \
+
+    LIST_VALID_TEXFORMAT_COMBINATIONS(RETURN_COMPATIBLE_FORMAT)
+}
+
 bool shaderType(GL2Encoder* ctx, GLenum type) {
     int glesMajorVersion = ctx->majorVersion();
     int glesMinorVersion = ctx->minorVersion();
@@ -873,4 +1219,323 @@
     return ss.str();
 }
 
+bool allowedFace(GLenum face) {
+    switch (face) {
+        case GL_FRONT:
+        case GL_BACK:
+        case GL_FRONT_AND_BACK:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedFunc(GLenum func) {
+    switch (func) {
+        case GL_NEVER:
+        case GL_ALWAYS:
+        case GL_LESS:
+        case GL_LEQUAL:
+        case GL_EQUAL:
+        case GL_GREATER:
+        case GL_GEQUAL:
+        case GL_NOTEQUAL:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedStencilOp(GLenum op) {
+    switch (op) {
+        case GL_KEEP:
+        case GL_ZERO:
+        case GL_REPLACE:
+        case GL_INCR:
+        case GL_DECR:
+        case GL_INVERT:
+        case GL_INCR_WRAP:
+        case GL_DECR_WRAP:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedBlendEquation(GLenum eq) {
+    switch (eq) {
+        case GL_FUNC_ADD:
+        case GL_FUNC_SUBTRACT:
+        case GL_FUNC_REVERSE_SUBTRACT:
+        case GL_MIN:
+        case GL_MAX:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedBlendFunc(GLenum func) {
+    switch (func) {
+        case GL_ZERO:
+        case GL_ONE:
+        case GL_SRC_COLOR:
+        case GL_ONE_MINUS_SRC_COLOR:
+        case GL_DST_COLOR:
+        case GL_ONE_MINUS_DST_COLOR:
+        case GL_SRC_ALPHA:
+        case GL_ONE_MINUS_SRC_ALPHA:
+        case GL_DST_ALPHA:
+        case GL_ONE_MINUS_DST_ALPHA:
+        case GL_CONSTANT_COLOR:
+        case GL_ONE_MINUS_CONSTANT_COLOR:
+        case GL_CONSTANT_ALPHA:
+        case GL_ONE_MINUS_CONSTANT_ALPHA:
+        case GL_SRC_ALPHA_SATURATE:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedCullFace(GLenum mode) {
+    switch (mode) {
+        case GL_FRONT:
+        case GL_BACK:
+        case GL_FRONT_AND_BACK:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedFrontFace(GLenum mode) {
+    switch (mode) {
+        case GL_CCW:
+        case GL_CW:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedEnable(int majorVersion, int minorVersion, GLenum cap) {
+    switch (cap) {
+        case GL_CULL_FACE:
+        case GL_POLYGON_OFFSET_FILL:
+        case GL_SAMPLE_ALPHA_TO_COVERAGE:
+        case GL_SAMPLE_COVERAGE:
+        case GL_SCISSOR_TEST:
+        case GL_STENCIL_TEST:
+        case GL_DEPTH_TEST:
+        case GL_BLEND:
+        case GL_DITHER:
+            return true;
+        case GL_PRIMITIVE_RESTART_FIXED_INDEX:
+        case GL_RASTERIZER_DISCARD:
+            return majorVersion >= 3;
+        case GL_SAMPLE_MASK:
+            return majorVersion >= 3 && minorVersion >= 1;
+		default:
+			return false;
+    }
+}
+
+bool allowedGetShader(GLenum pname) {
+	switch (pname) {
+		case GL_SHADER_TYPE:
+		case GL_DELETE_STATUS:
+		case GL_COMPILE_STATUS:
+		case GL_INFO_LOG_LENGTH:
+		case GL_SHADER_SOURCE_LENGTH:
+			return true;
+		default:
+			return false;
+	}
+}
+
+bool allowedShaderType(GLenum shadertype) {
+	switch (shadertype) {
+		case GL_VERTEX_SHADER:
+		case GL_FRAGMENT_SHADER:
+            return true;
+		default:
+			return false;
+	}
+}
+
+bool allowedPrecisionType(GLenum precisiontype) {
+	switch (precisiontype) {
+		case GL_LOW_FLOAT:
+		case GL_MEDIUM_FLOAT:
+		case GL_HIGH_FLOAT:
+		case GL_LOW_INT:
+		case GL_MEDIUM_INT:
+		case GL_HIGH_INT:
+            return true;
+		default:
+			return false;
+	}
+}
+
+bool allowedGetProgram(int majorVersion, int minorVersion, GLenum pname) {
+    switch (pname) {
+        case GL_DELETE_STATUS:
+        case GL_LINK_STATUS:
+        case GL_VALIDATE_STATUS:
+        case GL_INFO_LOG_LENGTH:
+        case GL_ATTACHED_SHADERS:
+        case GL_ACTIVE_ATTRIBUTES:
+        case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
+        case GL_ACTIVE_UNIFORMS:
+        case GL_ACTIVE_UNIFORM_MAX_LENGTH:
+            return true;
+        case GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
+        case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
+        case GL_PROGRAM_BINARY_LENGTH:
+        case GL_TRANSFORM_FEEDBACK_VARYINGS:
+        case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH:
+        case GL_ACTIVE_UNIFORM_BLOCKS:
+        case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH:
+            return majorVersion > 2;
+        case GL_COMPUTE_WORK_GROUP_SIZE:
+        case GL_PROGRAM_SEPARABLE:
+        case GL_ACTIVE_ATOMIC_COUNTER_BUFFERS:
+            return majorVersion > 2 && minorVersion > 0;
+        default:
+            return false;
+    }
+}
+
+bool allowedGetActiveUniforms(GLenum pname) {
+    switch (pname) {
+        case GL_UNIFORM_TYPE:
+        case GL_UNIFORM_SIZE:
+        case GL_UNIFORM_NAME_LENGTH:
+        case GL_UNIFORM_BLOCK_INDEX:
+        case GL_UNIFORM_OFFSET:
+        case GL_UNIFORM_ARRAY_STRIDE:
+        case GL_UNIFORM_MATRIX_STRIDE:
+        case GL_UNIFORM_IS_ROW_MAJOR:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedGetActiveUniformBlock(GLenum pname) {
+    switch (pname) {
+        case GL_UNIFORM_BLOCK_BINDING:
+        case GL_UNIFORM_BLOCK_DATA_SIZE:
+        case GL_UNIFORM_BLOCK_NAME_LENGTH:
+        case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
+        case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES:
+        case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
+        case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedGetVertexAttrib(GLenum pname) {
+    switch (pname) {
+        case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
+        case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
+        case GL_VERTEX_ATTRIB_ARRAY_SIZE:
+        case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
+        case GL_VERTEX_ATTRIB_ARRAY_TYPE:
+        case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
+        case GL_VERTEX_ATTRIB_ARRAY_INTEGER:
+        case GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
+        case GL_VERTEX_ATTRIB_BINDING:
+        case GL_VERTEX_ATTRIB_RELATIVE_OFFSET:
+        case GL_CURRENT_VERTEX_ATTRIB:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedGetRenderbufferParameter(GLenum pname) {
+    switch (pname) {
+        case GL_RENDERBUFFER_WIDTH:
+        case GL_RENDERBUFFER_HEIGHT:
+        case GL_RENDERBUFFER_INTERNAL_FORMAT:
+        case GL_RENDERBUFFER_RED_SIZE:
+        case GL_RENDERBUFFER_GREEN_SIZE:
+        case GL_RENDERBUFFER_BLUE_SIZE:
+        case GL_RENDERBUFFER_ALPHA_SIZE:
+        case GL_RENDERBUFFER_DEPTH_SIZE:
+        case GL_RENDERBUFFER_STENCIL_SIZE:
+        case GL_RENDERBUFFER_SAMPLES:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedQueryTarget(GLenum target) {
+    switch (target) {
+        case GL_ANY_SAMPLES_PASSED:
+        case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+        case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedQueryParam(GLenum pname) {
+    switch (pname) {
+        case GL_CURRENT_QUERY:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedQueryObjectParam(GLenum pname) {
+    switch (pname) {
+        case GL_QUERY_RESULT:
+        case GL_QUERY_RESULT_AVAILABLE:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedGetSyncParam(GLenum pname) {
+    switch (pname) {
+        case GL_OBJECT_TYPE:
+        case GL_SYNC_STATUS:
+        case GL_SYNC_CONDITION:
+        case GL_SYNC_FLAGS:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedHintTarget(GLenum target) {
+    switch (target) {
+        case GL_GENERATE_MIPMAP_HINT:
+        case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool allowedHintMode(GLenum mode) {
+    switch (mode) {
+        case GL_DONT_CARE:
+        case GL_NICEST:
+        case GL_FASTEST:
+            return true;
+        default:
+            return false;
+    }
+}
+
 } // namespace GLESv2Validation
diff --git a/system/GLESv2_enc/GLESv2Validation.h b/system/GLESv2_enc/GLESv2Validation.h
index a37bd58..4ac5787 100644
--- a/system/GLESv2_enc/GLESv2Validation.h
+++ b/system/GLESv2_enc/GLESv2Validation.h
@@ -33,6 +33,7 @@
 extern GLbitfield allBufferMapAccessFlags;
 bool bufferTarget(GL2Encoder* ctx, GLenum target);
 bool bufferParam(GL2Encoder* ctx, GLenum param);
+bool bufferUsage(GL2Encoder* ctx, GLenum usage);
 
 bool pixelStoreParam(GL2Encoder* ctx, GLenum param);
 bool pixelStoreValue(GLenum param, GLint value);
@@ -44,6 +45,7 @@
 
 bool readPixelsFormat(GLenum format);
 bool readPixelsType(GLenum type);
+bool pixelOp(GLenum format, GLenum type);
 
 bool vertexAttribType(GL2Encoder* ctx, GLenum type);
 
@@ -51,9 +53,11 @@
 bool blitFramebufferFormat(GLenum readFormat, GLenum drawFormat);
 
 bool textureTarget(GL2Encoder* ctx, GLenum target);
+bool textureParams(GL2Encoder* ctx, GLenum pname);
+bool samplerParams(GL2Encoder* ctx, GLenum pname);
+bool textureParamValue(GL2Encoder* ctx, GLenum pname, GLint intval, GLfloat floatval, GLenum enumval);
 
-GLsizei compressedTexImageSize(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
-
+bool isIntegerFormat(GLenum format);
 bool isCompressedFormat(GLenum internalformat);
 bool supportedCompressedFormat(GL2Encoder* ctx, GLenum internalformat);
 
@@ -70,6 +74,8 @@
 bool pixelFormat(GL2Encoder* ctx, GLenum format);
 
 bool pixelInternalFormat(GLenum internalformat);
+bool pixelSizedFormat(GL2Encoder* ctx, GLenum internalformat, GLenum format, GLenum type);
+void getCompatibleFormatTypeForInternalFormat(GLenum internalformat, GLenum* format_out, GLenum* type_out);
 
 bool shaderType(GL2Encoder* ctx, GLenum type);
 
@@ -77,6 +83,40 @@
 
 std::string vertexAttribIndexRangeErrorMsg(GL2Encoder* ctx, GLuint index);
 
+bool allowedFace(GLenum face);
+bool allowedFunc(GLenum func);
+bool allowedStencilOp(GLenum op);
+bool allowedBlendEquation(GLenum eq);
+bool allowedBlendFunc(GLenum func);
+
+bool allowedCullFace(GLenum mode);
+bool allowedFrontFace(GLenum mode);
+
+bool allowedEnable(int majorVersion, int minorVersion, GLenum mode);
+
+bool allowedGetShader(GLenum pname);
+
+bool allowedShaderType(GLenum shadertype);
+bool allowedPrecisionType(GLenum precisiontype);
+
+bool allowedGetProgram(int majorVersion, int minorVersion, GLenum pname);
+
+bool allowedGetActiveUniforms(GLenum pname) ;
+bool allowedGetActiveUniformBlock(GLenum pname) ;
+
+bool allowedGetVertexAttrib(GLenum pname) ;
+
+bool allowedGetRenderbufferParameter(GLenum pname);
+
+bool allowedQueryTarget(GLenum target);
+bool allowedQueryParam(GLenum pname);
+bool allowedQueryObjectParam(GLenum pname);
+
+bool allowedGetSyncParam(GLenum pname);
+
+bool allowedHintTarget(GLenum target);
+bool allowedHintMode(GLenum pname);
+
 } // namespace GLESv2Validation
 
 #endif
diff --git a/system/OpenglSystemCommon/CMakeLists.txt b/system/OpenglSystemCommon/CMakeLists.txt
index fe1a4bb..0149f8b 100644
--- a/system/OpenglSystemCommon/CMakeLists.txt
+++ b/system/OpenglSystemCommon/CMakeLists.txt
@@ -4,7 +4,7 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/Android.mk" "014a10fd6e8b1977ab8f66a6e7211136ea921d3c1692653988c894c7e87fee7a")
 set(OpenglSystemCommon_src FormatConversions.cpp HostConnection.cpp QemuPipeStream.cpp ProcessPipe.cpp AddressSpaceStream.cpp ThreadInfo_host.cpp)
 android_add_library(TARGET OpenglSystemCommon SHARED LICENSE Apache-2.0 SRC FormatConversions.cpp HostConnection.cpp QemuPipeStream.cpp ProcessPipe.cpp AddressSpaceStream.cpp ThreadInfo_host.cpp)
-target_include_directories(OpenglSystemCommon PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+target_include_directories(OpenglSystemCommon PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(OpenglSystemCommon PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM")
 target_compile_options(OpenglSystemCommon PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-unused-variable")
-target_link_libraries(OpenglSystemCommon PRIVATE android-emu-shared vulkan_enc gui androidemu cutils utils log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
+target_link_libraries(OpenglSystemCommon PRIVATE android-emu-shared vulkan_enc gui log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host cutils utils androidemu PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
diff --git a/system/OpenglSystemCommon/EmulatorFeatureInfo.h b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
index d0ae853..304ab36 100644
--- a/system/OpenglSystemCommon/EmulatorFeatureInfo.h
+++ b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
@@ -114,6 +114,9 @@
 // Vulkan async queue submit
 static const char kVulkanAsyncQueueSubmit[] = "ANDROID_EMU_vulkan_async_queue_submit";
 
+// A flag to _not_ ignore host opengl errors (now host opengl errors are ignored by default)
+static const char kGLESUseHostError[] = "ANDROID_EMU_gles_use_host_error";
+
 // Struct describing available emulator features
 struct EmulatorFeatureInfo {
 
diff --git a/system/OpenglSystemCommon/HostConnection.cpp b/system/OpenglSystemCommon/HostConnection.cpp
index 260bba8..1da1b71 100644
--- a/system/OpenglSystemCommon/HostConnection.cpp
+++ b/system/OpenglSystemCommon/HostConnection.cpp
@@ -355,7 +355,7 @@
     m_checksumHelper(),
     m_glExtensions(),
     m_grallocOnly(true),
-    m_noHostError(false),
+    m_noHostError(true),
     m_rendernodeFd(-1),
     m_rendernodeFdOwned(false) { }
 
@@ -800,8 +800,8 @@
 
 void HostConnection::queryAndSetNoErrorState(ExtendedRCEncoderContext* rcEnc) {
     std::string glExtensions = queryGLExtensions(rcEnc);
-    if (glExtensions.find(kGLESNoHostError) != std::string::npos) {
-        m_noHostError = true;
+    if (glExtensions.find(kGLESUseHostError) != std::string::npos) {
+        m_noHostError = false;
     }
 }
 
diff --git a/system/egl/CMakeLists.txt b/system/egl/CMakeLists.txt
index bae5ceb..cf649a2 100644
--- a/system/egl/CMakeLists.txt
+++ b/system/egl/CMakeLists.txt
@@ -4,7 +4,7 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/egl/Android.mk" "0d72068bb429ec9da33f7086a879a08b540b887538f21d0a9f468cead76c45df")
 set(EGL_emulation_src eglDisplay.cpp egl.cpp ClientAPIExts.cpp)
 android_add_library(TARGET EGL_emulation SHARED LICENSE Apache-2.0 SRC eglDisplay.cpp egl.cpp ClientAPIExts.cpp)
-target_include_directories(EGL_emulation PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+target_include_directories(EGL_emulation PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(EGL_emulation PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"EGL_emulation\"" "-DEGL_EGLEXT_PROTOTYPES")
 target_compile_options(EGL_emulation PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-gnu-designator")
-target_link_libraries(EGL_emulation PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui androidemu cutils utils log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
+target_link_libraries(EGL_emulation PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host cutils utils androidemu PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
diff --git a/system/egl/egl.cpp b/system/egl/egl.cpp
index 2f88fcd..601ad9d 100644
--- a/system/egl/egl.cpp
+++ b/system/egl/egl.cpp
@@ -1859,41 +1859,25 @@
                 context->minorVersion,
                 context->deviceMajorVersion,
                 context->deviceMinorVersion);
-            // Get caps for indexed buffers from host.
-            // Some need a current context.
-            int max_transform_feedback_separate_attribs = 0;
-            int max_uniform_buffer_bindings = 0;
-            int max_atomic_counter_buffer_bindings = 0;
-            int max_shader_storage_buffer_bindings = 0;
-            int max_vertex_attrib_bindings = 0;
-            int max_color_attachments = 1;
-            int max_draw_buffers = 1;
-            if (context->majorVersion > 2) {
-                s_display.gles2_iface()->getIntegerv(
-                        GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &max_transform_feedback_separate_attribs);
-                s_display.gles2_iface()->getIntegerv(
-                        GL_MAX_UNIFORM_BUFFER_BINDINGS, &max_uniform_buffer_bindings);
-                if (context->minorVersion > 0) {
-                    s_display.gles2_iface()->getIntegerv(
-                            GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &max_atomic_counter_buffer_bindings);
-                    s_display.gles2_iface()->getIntegerv(
-                            GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &max_shader_storage_buffer_bindings);
-                    s_display.gles2_iface()->getIntegerv(
-                            GL_MAX_VERTEX_ATTRIB_BINDINGS, &max_vertex_attrib_bindings);
-                }
-                s_display.gles2_iface()->getIntegerv(
-                        GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
-                s_display.gles2_iface()->getIntegerv(
-                        GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
+            hostCon->gl2Encoder()->setClientState(contextState);
+            if (context->majorVersion > 1) {
+                HostDriverCaps caps = s_display.getHostDriverCaps(
+                    context->majorVersion,
+                    context->minorVersion);
+                contextState->initFromCaps(caps);
+            } else {
+                // Just put some stuff here to make gles1 happy
+                HostDriverCaps gles1Caps = {
+                    .max_vertex_attribs = 16,
+                    .max_combined_texture_image_units = 8,
+                    .max_color_attachments = 8,
+
+                    .max_texture_size = 4096,
+                    .max_texture_size_cube_map = 2048,
+                    .max_renderbuffer_size = 4096,
+                };
+                contextState->initFromCaps(gles1Caps);
             }
-            contextState->initFromCaps(
-                    max_transform_feedback_separate_attribs,
-                    max_uniform_buffer_bindings,
-                    max_atomic_counter_buffer_bindings,
-                    max_shader_storage_buffer_bindings,
-                    max_vertex_attrib_bindings,
-                    max_color_attachments,
-                    max_draw_buffers);
         }
 
         // update the client state, share group, and version
diff --git a/system/egl/eglDisplay.cpp b/system/egl/eglDisplay.cpp
index 2508f48..64f7de4 100644
--- a/system/egl/eglDisplay.cpp
+++ b/system/egl/eglDisplay.cpp
@@ -26,6 +26,8 @@
 
 #include <dlfcn.h>
 
+#include <GLES3/gl31.h>
+
 static const int systemEGLVersionMajor = 1;
 static const int systemEGLVersionMinor = 4;
 static const char systemEGLVendor[] = "Google Android emulator";
@@ -73,7 +75,9 @@
     m_gles2_iface(NULL),
     m_versionString(NULL),
     m_vendorString(NULL),
-    m_extensionString(NULL)
+    m_extensionString(NULL),
+    m_hostDriverCaps_knownMajorVersion(0),
+    m_hostDriverCaps_knownMinorVersion(0)
 {
     pthread_mutex_init(&m_lock, NULL);
     pthread_mutex_init(&m_ctxLock, NULL);
@@ -171,7 +175,6 @@
 
         uint32_t nInts = m_numConfigAttribs * (m_numConfigs + 1);
         EGLint tmp_buf[nInts];
-        uint32_t configCount = nInts - m_numConfigAttribs;
 
         m_configs = new EGLint[nInts-m_numConfigAttribs];
 
@@ -654,3 +657,53 @@
     pthread_mutex_unlock(&m_surfaceLock);
     return res;
 }
+
+HostDriverCaps eglDisplay::getHostDriverCaps(int majorVersion, int minorVersion) {
+    pthread_mutex_lock(&m_lock);
+    if (majorVersion <= m_hostDriverCaps_knownMajorVersion &&
+        minorVersion <= m_hostDriverCaps_knownMinorVersion) {
+        pthread_mutex_unlock(&m_lock);
+        return m_hostDriverCaps;
+    }
+
+    memset(&m_hostDriverCaps, 0x0, sizeof(m_hostDriverCaps));
+
+    // Can we query gles2?
+    if (majorVersion >= 1) {
+        m_gles2_iface->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &m_hostDriverCaps.max_vertex_attribs);
+        m_gles2_iface->getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &m_hostDriverCaps.max_combined_texture_image_units);
+        m_gles2_iface->getIntegerv(GL_MAX_COLOR_ATTACHMENTS, &m_hostDriverCaps.max_color_attachments);
+
+        m_gles2_iface->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_hostDriverCaps.max_texture_size);
+        m_gles2_iface->getIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &m_hostDriverCaps.max_texture_size_cube_map);
+        m_gles2_iface->getIntegerv(GL_MAX_RENDERBUFFER_SIZE, &m_hostDriverCaps.max_renderbuffer_size);
+        m_hostDriverCaps_knownMajorVersion = 2;
+    }
+
+    // Can we query gles3.0?
+    if (majorVersion >= 3) {
+        m_gles2_iface->getIntegerv(GL_MAX_DRAW_BUFFERS, &m_hostDriverCaps.max_draw_buffers);
+        m_gles2_iface->getIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &m_hostDriverCaps.ubo_offset_alignment);
+        m_gles2_iface->getIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &m_hostDriverCaps.max_uniform_buffer_bindings);
+        m_gles2_iface->getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &m_hostDriverCaps.max_transform_feedback_separate_attribs);
+        m_gles2_iface->getIntegerv(GL_MAX_3D_TEXTURE_SIZE, &m_hostDriverCaps.max_texture_size_3d);
+        m_gles2_iface->getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &m_hostDriverCaps.max_array_texture_layers);
+
+        m_hostDriverCaps_knownMajorVersion = 3;
+
+        // Can we query gles3.1?
+        if (minorVersion >= 1) {
+            m_gles2_iface->getIntegerv(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &m_hostDriverCaps.max_atomic_counter_buffer_bindings);
+            m_gles2_iface->getIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &m_hostDriverCaps.max_shader_storage_buffer_bindings);
+            m_gles2_iface->getIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &m_hostDriverCaps.max_vertex_attrib_bindings);
+            m_gles2_iface->getIntegerv(GL_MAX_VERTEX_ATTRIB_STRIDE, &m_hostDriverCaps.max_vertex_attrib_stride);
+            m_gles2_iface->getIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &m_hostDriverCaps.ssbo_offset_alignment);
+            m_hostDriverCaps_knownMinorVersion = 1;
+        }
+    }
+
+    pthread_mutex_unlock(&m_lock);
+
+    return m_hostDriverCaps;
+}
+
diff --git a/system/egl/eglDisplay.h b/system/egl/eglDisplay.h
index 951075d..25494e1 100644
--- a/system/egl/eglDisplay.h
+++ b/system/egl/eglDisplay.h
@@ -22,6 +22,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include "EGLClientIface.h"
+#include "GLClientState.h"
 
 #if __cplusplus >= 201103L
 #include <unordered_set>
@@ -76,6 +77,10 @@
 
     bool isContext(EGLContext ctx);
     bool isSurface(EGLSurface ctx);
+
+    // Needs a current context (put this near eglMakeCurrent)
+    HostDriverCaps getHostDriverCaps(int majorVersion, int minorVersion);
+
 private:
     EGLClient_glesInterface *loadGLESClientAPI(const char *libName,
                                                EGLClient_eglInterface *eglIface,
@@ -118,6 +123,10 @@
     EGLSurfaceSet m_surfaces;
     pthread_mutex_t m_ctxLock;
     pthread_mutex_t m_surfaceLock;
+
+    int m_hostDriverCaps_knownMajorVersion;
+    int m_hostDriverCaps_knownMinorVersion;
+    HostDriverCaps m_hostDriverCaps;
 };
 
 #endif
diff --git a/system/gralloc/CMakeLists.txt b/system/gralloc/CMakeLists.txt
index 0bab42f..c090eb3 100644
--- a/system/gralloc/CMakeLists.txt
+++ b/system/gralloc/CMakeLists.txt
@@ -4,17 +4,17 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/gralloc/Android.mk" "4c44a906197808b25fde953a476e026aac725e5286e0f91d5f8a3083f40f49da")
 set(gralloc.goldfish_src gralloc_old.cpp)
 android_add_library(TARGET gralloc.goldfish SHARED LICENSE Apache-2.0 SRC gralloc_old.cpp)
-target_include_directories(gralloc.goldfish PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+target_include_directories(gralloc.goldfish PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(gralloc.goldfish PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"gralloc_goldfish\"")
 target_compile_options(gralloc.goldfish PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-Wno-gnu-designator")
-target_link_libraries(gralloc.goldfish PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui androidemu cutils utils log GLESv2_enc _renderControl_enc GLESv1_enc OpenglCodecCommon_host PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
+target_link_libraries(gralloc.goldfish PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui log GLESv2_enc _renderControl_enc GLESv1_enc OpenglCodecCommon_host cutils utils androidemu PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
 # This is an autogenerated file! Do not edit!
 # instead run make from .../device/generic/goldfish-opengl
 # which will re-generate this file.
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/gralloc/Android.mk" "4c44a906197808b25fde953a476e026aac725e5286e0f91d5f8a3083f40f49da")
 set(gralloc.ranchu_src gralloc_old.cpp)
 android_add_library(TARGET gralloc.ranchu SHARED LICENSE Apache-2.0 SRC gralloc_old.cpp)
-target_include_directories(gralloc.ranchu PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+target_include_directories(gralloc.ranchu PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(gralloc.ranchu PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"gralloc_ranchu\"")
 target_compile_options(gralloc.ranchu PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-Wno-gnu-designator")
-target_link_libraries(gralloc.ranchu PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui androidemu cutils utils log GLESv2_enc _renderControl_enc GLESv1_enc OpenglCodecCommon_host PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
+target_link_libraries(gralloc.ranchu PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui log GLESv2_enc _renderControl_enc GLESv1_enc OpenglCodecCommon_host cutils utils androidemu PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
diff --git a/system/renderControl_enc/CMakeLists.txt b/system/renderControl_enc/CMakeLists.txt
index dcb6cda..a2b4221 100644
--- a/system/renderControl_enc/CMakeLists.txt
+++ b/system/renderControl_enc/CMakeLists.txt
@@ -4,7 +4,7 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc/Android.mk" "780a007ac7a3d2255372ddf40e03aeb10e4c759343d2532f6ddf769f4df73810")
 set(_renderControl_enc_src renderControl_client_context.cpp renderControl_enc.cpp renderControl_entry.cpp)
 android_add_library(TARGET _renderControl_enc SHARED LICENSE Apache-2.0 SRC renderControl_client_context.cpp renderControl_enc.cpp renderControl_entry.cpp)
-target_include_directories(_renderControl_enc PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+target_include_directories(_renderControl_enc PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
 target_compile_definitions(_renderControl_enc PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM")
 target_compile_options(_renderControl_enc PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-unused-function")
-target_link_libraries(_renderControl_enc PRIVATE OpenglCodecCommon_host cutils utils log android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
+target_link_libraries(_renderControl_enc PRIVATE OpenglCodecCommon_host cutils utils log androidemu android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
diff --git a/system/vulkan/CMakeLists.txt b/system/vulkan/CMakeLists.txt
index a364b2c..eb2f612 100644
--- a/system/vulkan/CMakeLists.txt
+++ b/system/vulkan/CMakeLists.txt
@@ -4,7 +4,7 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/vulkan/Android.mk" "5cb873c72cc859fac3800961059c1b203ed1abb400ee643178c18e04961d49e8")
 set(vulkan.ranchu_src func_table.cpp goldfish_vulkan.cpp)
 android_add_library(TARGET vulkan.ranchu SHARED LICENSE Apache-2.0 SRC func_table.cpp goldfish_vulkan.cpp)
-target_include_directories(vulkan.ranchu PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/system/vulkan ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/host/include)
+target_include_directories(vulkan.ranchu PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/system/vulkan ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/host/include)
 target_compile_definitions(vulkan.ranchu PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"goldfish_vulkan\"" "-DVK_USE_PLATFORM_ANDROID_KHR" "-DVK_NO_PROTOTYPES")
 target_compile_options(vulkan.ranchu PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-fvisibility=hidden" "-fstrict-aliasing" "-Wno-unused-function")
-target_link_libraries(vulkan.ranchu PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui androidemu cutils utils log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
+target_link_libraries(vulkan.ranchu PRIVATE OpenglSystemCommon android-emu-shared vulkan_enc gui log _renderControl_enc GLESv2_enc GLESv1_enc OpenglCodecCommon_host cutils utils androidemu PRIVATE gralloc_cb_host GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
diff --git a/system/vulkan_enc/CMakeLists.txt b/system/vulkan_enc/CMakeLists.txt
index 599f692..4488658 100644
--- a/system/vulkan_enc/CMakeLists.txt
+++ b/system/vulkan_enc/CMakeLists.txt
@@ -4,7 +4,7 @@
 android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc/Android.mk" "103f3f10c8af73d3d5c4263c4faa5bb70ebdbfbc9ed6f068a338d57344e2aa45")
 set(vulkan_enc_src AndroidHardwareBuffer.cpp HostVisibleMemoryVirtualization.cpp Resources.cpp Validation.cpp VulkanStreamGuest.cpp VulkanHandleMapping.cpp ResourceTracker.cpp VkEncoder.cpp goldfish_vk_extension_structs_guest.cpp goldfish_vk_marshaling_guest.cpp goldfish_vk_deepcopy_guest.cpp goldfish_vk_handlemap_guest.cpp goldfish_vk_transform_guest.cpp)
 android_add_library(TARGET vulkan_enc SHARED LICENSE Apache-2.0 SRC AndroidHardwareBuffer.cpp HostVisibleMemoryVirtualization.cpp Resources.cpp Validation.cpp VulkanStreamGuest.cpp VulkanHandleMapping.cpp ResourceTracker.cpp VkEncoder.cpp goldfish_vk_extension_structs_guest.cpp goldfish_vk_marshaling_guest.cpp goldfish_vk_deepcopy_guest.cpp goldfish_vk_handlemap_guest.cpp goldfish_vk_transform_guest.cpp)
-target_include_directories(vulkan_enc PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/host/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/host/include/vulkan)
+target_include_directories(vulkan_enc PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/host/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/host/include/vulkan)
 target_compile_definitions(vulkan_enc PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"goldfish_vulkan\"" "-DVK_ANDROID_native_buffer" "-DVK_GOOGLE_address_space" "-DVK_USE_PLATFORM_ANDROID_KHR" "-DVK_NO_PROTOTYPES" "-D__ANDROID_API__=28")
 target_compile_options(vulkan_enc PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-Werror" "-fstrict-aliasing")
-target_link_libraries(vulkan_enc PRIVATE gui log android-emu-shared androidemu cutils utils _renderControl_enc OpenglCodecCommon_host PRIVATE GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file
+target_link_libraries(vulkan_enc PRIVATE gui log android-emu-shared _renderControl_enc OpenglCodecCommon_host cutils utils androidemu PRIVATE GoldfishAddressSpace_host qemupipe_host)
\ No newline at end of file