Initial version of external_oes texture support and unit test

Review URL: https://codereview.chromium.org/1451683002
diff --git a/include/gpu/GrTypesPriv.h b/include/gpu/GrTypesPriv.h
index 9202742..6135c14 100644
--- a/include/gpu/GrTypesPriv.h
+++ b/include/gpu/GrTypesPriv.h
@@ -25,8 +25,9 @@
     kMat33f_GrSLType,
     kMat44f_GrSLType,
     kSampler2D_GrSLType,
+    kSamplerExternal_GrSLType,
 
-    kLast_GrSLType = kSampler2D_GrSLType
+    kLast_GrSLType = kSamplerExternal_GrSLType
 };
 static const int kGrSLTypeCount = kLast_GrSLType + 1;
 
@@ -63,7 +64,7 @@
  */
 static inline int GrSLTypeVectorCount(GrSLType type) {
     SkASSERT(type >= 0 && type < static_cast<GrSLType>(kGrSLTypeCount));
-    static const int kCounts[] = { -1, 1, 2, 3, 4, -1, -1, -1 };
+    static const int kCounts[] = { -1, 1, 2, 3, 4, -1, -1, -1, -1 };
     return kCounts[type];
 
     GR_STATIC_ASSERT(0 == kVoid_GrSLType);
@@ -74,6 +75,7 @@
     GR_STATIC_ASSERT(5 == kMat33f_GrSLType);
     GR_STATIC_ASSERT(6 == kMat44f_GrSLType);
     GR_STATIC_ASSERT(7 == kSampler2D_GrSLType);
+    GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType);
     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kCounts) == kGrSLTypeCount);
 }
 
@@ -102,7 +104,8 @@
     GR_STATIC_ASSERT(5 == kMat33f_GrSLType);
     GR_STATIC_ASSERT(6 == kMat44f_GrSLType);
     GR_STATIC_ASSERT(7 == kSampler2D_GrSLType);
-    GR_STATIC_ASSERT(8 == kGrSLTypeCount);
+    GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType);
+    GR_STATIC_ASSERT(9 == kGrSLTypeCount);
 }
 //////////////////////////////////////////////////////////////////////////////
 
diff --git a/include/gpu/gl/GrGLFunctions.h b/include/gpu/gl/GrGLFunctions.h
index a8a721e..1086186 100644
--- a/include/gpu/gl/GrGLFunctions.h
+++ b/include/gpu/gl/GrGLFunctions.h
@@ -69,6 +69,7 @@
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawBufferProc)(GrGLenum mode);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawBuffersProc)(GrGLsizei n, const GrGLenum* bufs);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawElementsProc)(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid* indices);
+typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEGLImageTargetTexture2DProc)(GrGLenum target, GrGLeglImage image);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEnableProc)(GrGLenum cap);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEnableVertexAttribArrayProc)(GrGLuint index);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEndQueryProc)(GrGLenum target);
@@ -352,8 +353,8 @@
 /** EGL functions */
 typedef const char* (GR_GL_FUNCTION_TYPE* GrEGLQueryStringProc)(GrEGLDisplay dpy, GrEGLint name);
 typedef GrEGLDisplay (GR_GL_FUNCTION_TYPE* GrEGLGetCurrentDisplayProc)();
-typedef GrEGLImageKHR (GR_GL_FUNCTION_TYPE* GrEGLCreateImageProc)(GrEGLDisplay dpy, GrEGLContext ctx, GrEGLenum target, GrEGLClientBuffer buffer, const GrEGLint *attrib_list);
-typedef GrEGLBoolean (GR_GL_FUNCTION_TYPE* GrEGLDestroyImageProc)(GrEGLDisplay dpy, GrEGLImageKHR image);
+typedef GrEGLImage (GR_GL_FUNCTION_TYPE* GrEGLCreateImageProc)(GrEGLDisplay dpy, GrEGLContext ctx, GrEGLenum target, GrEGLClientBuffer buffer, const GrEGLint *attrib_list);
+typedef GrEGLBoolean (GR_GL_FUNCTION_TYPE* GrEGLDestroyImageProc)(GrEGLDisplay dpy, GrEGLImage image);
 }  // extern "C"
 
 #endif
diff --git a/include/gpu/gl/GrGLInterface.h b/include/gpu/gl/GrGLInterface.h
index ec27ace..31429a8 100644
--- a/include/gpu/gl/GrGLInterface.h
+++ b/include/gpu/gl/GrGLInterface.h
@@ -498,8 +498,8 @@
         GLPtr<GrGLObjectLabelProc> fObjectLabel;
 
         /* EGL functions */
-        GLPtr<GrEGLCreateImageProc> fCreateImage;
-        GLPtr<GrEGLDestroyImageProc> fDestroyImage;
+        GLPtr<GrEGLCreateImageProc> fEGLCreateImage;
+        GLPtr<GrEGLDestroyImageProc> fEGLDestroyImage;
     } fFunctions;
 
     // Per-GL func callback
diff --git a/include/gpu/gl/GrGLTypes.h b/include/gpu/gl/GrGLTypes.h
index 275eba5..248ce88 100644
--- a/include/gpu/gl/GrGLTypes.h
+++ b/include/gpu/gl/GrGLTypes.h
@@ -57,12 +57,12 @@
 typedef signed long int GrGLintptr;
 typedef signed long int GrGLsizeiptr;
 #endif
-
+typedef void* GrGLeglImage;
 /**
  * EGL types.
  */
 
-typedef void* GrEGLImageKHR;
+typedef void* GrEGLImage;
 typedef void* GrEGLDisplay;
 typedef void* GrEGLContext;
 typedef void* GrEGLClientBuffer;
diff --git a/include/gpu/gl/SkGLContext.h b/include/gpu/gl/SkGLContext.h
index 75cfcfe..3420a47 100644
--- a/include/gpu/gl/SkGLContext.h
+++ b/include/gpu/gl/SkGLContext.h
@@ -14,8 +14,9 @@
 /**
  * Create an offscreen opengl context with an RGBA8 / 8bit stencil FBO.
  * Provides a GrGLInterface struct of function pointers for the context.
+ * This class is intended for Skia's testing needs and not for general
+ * use.
  */
-
 class SK_API SkGLContext : public SkRefCnt {
 public:
     ~SkGLContext() override;
@@ -36,6 +37,16 @@
 
     void makeCurrent() const;
 
+    /** Used for testing EGLImage integration. Take a GL_TEXTURE_2D and wraps it in an EGL Image */
+    virtual GrEGLImage texture2DToEGLImage(GrGLuint /*texID*/) const { return 0; }
+    virtual void destroyEGLImage(GrEGLImage) const {}
+
+    /**
+     * Used for testing EGLImage integration. Takes a EGLImage and wraps it in a
+     * GL_TEXTURE_EXTERNAL_OES.
+     */
+    virtual GrGLuint eglImageToExternalTexture(GrEGLImage) const { return 0; }
+
     /**
      * The only purpose of this function it to provide a means of scheduling
      * work on the GPU (since all of the subclasses create primary buffers for
@@ -59,6 +70,12 @@
      */
     void testAbandon();
 
+    /**
+     * Creates a new GL context of the same type and makes the returned context current
+     * (if not null).
+     */
+    virtual SkGLContext* createNew() const { return nullptr; }
+
     class GLFenceSync;  // SkGpuFenceSync implementation that uses the OpenGL functionality.
 
 protected:
diff --git a/include/gpu/gl/angle/SkANGLEGLContext.h b/include/gpu/gl/angle/SkANGLEGLContext.h
index 8d5f830..7858fff 100644
--- a/include/gpu/gl/angle/SkANGLEGLContext.h
+++ b/include/gpu/gl/angle/SkANGLEGLContext.h
@@ -27,6 +27,10 @@
         }
         return ctx;
     }
+    GrEGLImage texture2DToEGLImage(GrGLuint texID) const override;
+    void destroyEGLImage(GrEGLImage) const override;
+    GrGLuint eglImageToExternalTexture(GrEGLImage) const override;
+    SkGLContext* createNew() const override;
 
     // The param is an EGLNativeDisplayType and the return is an EGLDispay.
     static void* GetD3DEGLDisplay(void* nativeDisplay, bool useGLBackend);
@@ -42,6 +46,7 @@
     void* fContext;
     void* fDisplay;
     void* fSurface;
+    bool  fIsGLBackend;
 };
 
 #endif
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index 3156814..38213e6 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -202,7 +202,7 @@
         if (rt->renderTargetPriv().attachStencilAttachment(stencil)) {
             if (newStencil) {
                 // Right now we're clearing the stencil attachment here after it is
-                // attached to an RT for the first time. When we start matching
+                // attached to a RT for the first time. When we start matching
                 // stencil buffers with smaller color targets this will no longer
                 // be correct because it won't be guaranteed to clear the entire
                 // sb.
diff --git a/src/gpu/gl/GrGLAssembleInterface.cpp b/src/gpu/gl/GrGLAssembleInterface.cpp
index e606f4a..dbc54ad 100644
--- a/src/gpu/gl/GrGLAssembleInterface.cpp
+++ b/src/gpu/gl/GrGLAssembleInterface.cpp
@@ -14,7 +14,7 @@
 #define GET_PROC_SUFFIX(F, S) functions->f ## F = (GrGL ## F ## Proc) get(ctx, "gl" #F #S)
 #define GET_PROC_LOCAL(F) GrGL ## F ## Proc F = (GrGL ## F ## Proc) get(ctx, "gl" #F)
 
-#define GET_EGL_PROC_SUFFIX(F, S) functions->f ## F = (GrEGL ## F ## Proc) get(ctx, "egl" #F #S)
+#define GET_EGL_PROC_SUFFIX(F, S) functions->fEGL ## F = (GrEGL ## F ## Proc) get(ctx, "egl" #F #S)
 
 const GrGLInterface* GrGLAssembleInterface(void* ctx, GrGLGetProc get) {
     GET_PROC_LOCAL(GetString);
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 28401fe..a768b37 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -46,6 +46,7 @@
     fUseNonVBOVertexAndIndexDynamicData = false;
     fIsCoreProfile = false;
     fBindFragDataLocationSupport = false;
+    fExternalTextureSupport = false;
     fSRGBWriteControl = false;
     fRGBA8888PixelsOpsAreSlow = false;
     fPartialFBOReadIsSlow = false;
@@ -259,6 +260,16 @@
     fBindUniformLocationSupport = false;
 #endif
 
+    if (ctxInfo.hasExtension("GL_OES_EGL_image_external")) {
+        if (ctxInfo.glslGeneration() == k110_GrGLSLGeneration) {
+            fExternalTextureSupport = true;
+        } else if (ctxInfo.hasExtension("GL_OES_EGL_image_external_essl3") ||
+                   ctxInfo.hasExtension("OES_EGL_image_external_essl3")) {
+            // At least one driver has been found that has this extension without the "GL_" prefix.
+            fExternalTextureSupport = true;
+        }
+    }
+
 #ifdef SK_BUILD_FOR_WIN
     // We're assuming that on Windows Chromium we're using ANGLE.
     bool isANGLE = kANGLE_GrGLDriver == ctxInfo.driver() ||
@@ -588,13 +599,21 @@
         glslCaps->fSecondaryOutputExtensionString = "GL_EXT_blend_func_extended";
     }
 
+    if (fExternalTextureSupport) {
+        if (ctxInfo.glslGeneration() == k110_GrGLSLGeneration) {
+            glslCaps->fExternalTextureExtensionString = "GL_OES_EGL_image_external";
+        } else {
+            glslCaps->fExternalTextureExtensionString = "GL_OES_EGL_image_external_essl3";
+        }
+    }
+
     // The Tegra3 compiler will sometimes never return if we have min(abs(x), 1.0), so we must do
     // the abs first in a separate expression.
     if (kTegra3_GrGLRenderer == ctxInfo.renderer()) {
         glslCaps->fCanUseMinAndAbsTogether = false;
     }
 
-    // On Intel GPU there is an issue where it reads the second arguement to atan "- %s.x" as an int
+    // On Intel GPU there is an issue where it reads the second argument to atan "- %s.x" as an int
     // thus must us -1.0 * %s.x to work correctly
     if (kIntel_GrGLVendor == ctxInfo.vendor()) {
         glslCaps->fMustForceNegatedAtanParamToFloat = true;
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 5f1ca3e..0ec6c85 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -232,6 +232,9 @@
 
     bool bindUniformLocationSupport() const { return fBindUniformLocationSupport; }
 
+    /// Are textures with GL_TEXTURE_EXTERNAL_OES type supported.
+    bool externalTextureSupport() const { return fExternalTextureSupport; }
+
     /**
      * Is there support for enabling/disabling sRGB writes for sRGB-capable color attachments?
      * If false this does not mean sRGB is not supported but rather that if it is supported
@@ -360,6 +363,7 @@
     bool fRGBA8888PixelsOpsAreSlow : 1;
     bool fPartialFBOReadIsSlow : 1;
     bool fBindUniformLocationSupport : 1;
+    bool fExternalTextureSupport : 1;
 
     struct ReadPixelsSupportedFormat {
         GrGLenum fFormat;
diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h
index 603a319..dfab5e7 100644
--- a/src/gpu/gl/GrGLDefines.h
+++ b/src/gpu/gl/GrGLDefines.h
@@ -928,8 +928,18 @@
 #define GR_GL_PROGRAM_PIPELINE                              0x82E4
 #define GR_GL_SAMPLER                                       0x82E6
 
+/* GL_OES_EGL_image_external */
+#define GR_GL_TEXTURE_EXTERNAL                              0x8D65
+
 /* EGL Defines */
 #define GR_EGL_NO_DISPLAY                                   ((GrEGLDisplay)0)
 #define GR_EGL_EXTENSIONS                                   0x3055
+#define GR_EGL_GL_TEXTURE_2D                                0x30B1
+#define GR_EGL_GL_TEXTURE_LEVEL                             0x30BC
+#define GR_EGL_IMAGE_PRESERVED                              0x30D2
+#define GR_EGL_FALSE                                        0x0
+#define GR_EGL_TRUE                                         0x1
+#define GR_EGL_NONE                                         0x3038
+#define GR_EGL_NO_IMAGE                                    ((GrEGLImage)0)
 
 #endif
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 2bd36b6..51145c8 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -233,7 +233,7 @@
         fPathRendering.reset(new GrGLPathRendering(this));
     }
 
-    this->createCopyProgram();
+    this->createCopyPrograms();
 }
 
 GrGLGpu::~GrGLGpu() {
@@ -253,12 +253,13 @@
         GL_CALL(DeleteFramebuffers(1, &fStencilClearFBOID));
     }
 
-    if (0 != fCopyProgram.fArrayBuffer) {
-        GL_CALL(DeleteBuffers(1, &fCopyProgram.fArrayBuffer));
+    for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) {
+        if (0 != fCopyPrograms[i].fProgram) {
+            GL_CALL(DeleteProgram(fCopyPrograms[i].fProgram));
+        }
     }
-
-    if (0 != fCopyProgram.fProgram) {
-        GL_CALL(DeleteProgram(fCopyProgram.fProgram));
+    if (0 != fCopyProgramArrayBuffer) {
+        GL_CALL(DeleteBuffers(1, &fCopyProgramArrayBuffer));
     }
 
     delete fProgramCache;
@@ -271,8 +272,10 @@
     fTempSrcFBOID = 0;
     fTempDstFBOID = 0;
     fStencilClearFBOID = 0;
-    fCopyProgram.fArrayBuffer = 0;
-    fCopyProgram.fProgram = 0;
+    fCopyProgramArrayBuffer = 0;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) {
+        fCopyPrograms[i].fProgram = 0;
+    }
     if (this->glCaps().shaderCaps()->pathRenderingSupport()) {
         this->glPathRendering()->abandonGpuResources();
     }
@@ -429,6 +432,9 @@
         return nullptr;
     }
 
+    // next line relies on GrBackendTextureDesc's flags matching GrTexture's
+    bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag);
+
     GrGLTexture::IDDesc idDesc;
     GrSurfaceDesc surfDesc;
 
@@ -439,6 +445,15 @@
 #else
     idDesc.fInfo = *info;
 #endif
+    if (GR_GL_TEXTURE_EXTERNAL == idDesc.fInfo.fTarget) {
+        if (renderTarget) {
+            // This combination is not supported.
+            return nullptr;
+        }
+        if (!this->glCaps().externalTextureSupport()) {
+            return nullptr;
+        }
+    }
 
     switch (ownership) {
         case kAdopt_GrWrapOwnership:
@@ -449,13 +464,11 @@
             break;
     }    
 
-    // next line relies on GrBackendTextureDesc's flags matching GrTexture's
     surfDesc.fFlags = (GrSurfaceFlags) desc.fFlags;
     surfDesc.fWidth = desc.fWidth;
     surfDesc.fHeight = desc.fHeight;
     surfDesc.fConfig = desc.fConfig;
     surfDesc.fSampleCnt = SkTMin(desc.fSampleCnt, this->caps()->maxSampleCount());
-    bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag);
     // FIXME:  this should be calling resolve_origin(), but Chrome code is currently
     // assuming the old behaviour, which is that backend textures are always
     // BottomLeft, even for non-RT's.  Once Chrome is fixed, change this to:
@@ -524,6 +537,12 @@
     // into it. We could use glDrawPixels on GLs that have it, but we don't today.
     if (!dstSurface->asTexture()) {
         ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
+    } else {
+        GrGLTexture* texture = static_cast<GrGLTexture*>(dstSurface->asTexture());
+        if (GR_GL_TEXTURE_2D != texture->target()) {
+             // We don't currently support writing pixels to non-TEXTURE_2D textures.
+             return false;
+        }
     }
 
     if (GrPixelConfigIsSRGB(dstSurface->config()) != GrPixelConfigIsSRGB(srcConfig)) {
@@ -585,6 +604,11 @@
         return false;
     }
 
+    // Write pixels is only implemented for TEXTURE_2D textures
+    if (GR_GL_TEXTURE_2D != glTex->target()) {
+        return false;
+    }
+
     this->setScratchTextureUnit();
     GL_CALL(BindTexture(glTex->target(), glTex->textureID()));
 
@@ -2752,11 +2776,10 @@
     fHWBoundTextureUniqueIDs[lastUnitIdx] = SK_InvalidUniqueID;
 }
 
-namespace {
 // Determines whether glBlitFramebuffer could be used between src and dst.
-inline bool can_blit_framebuffer(const GrSurface* dst,
-                                 const GrSurface* src,
-                                 const GrGLGpu* gpu) {
+static inline bool can_blit_framebuffer(const GrSurface* dst,
+                                        const GrSurface* src,
+                                        const GrGLGpu* gpu) {
     if (gpu->glCaps().isConfigRenderable(dst->config(), dst->desc().fSampleCnt > 0) &&
         gpu->glCaps().isConfigRenderable(src->config(), src->desc().fSampleCnt > 0) &&
         gpu->glCaps().usesMSAARenderBuffers()) {
@@ -2766,15 +2789,23 @@
             (src->desc().fSampleCnt > 0 || src->config() != dst->config())) {
            return false;
         }
+        const GrGLTexture* dstTex = static_cast<const GrGLTexture*>(dst->asTexture());
+        if (dstTex && dstTex->target() != GR_GL_TEXTURE_2D) {
+            return false;
+        }
+        const GrGLTexture* srcTex = static_cast<const GrGLTexture*>(dst->asTexture());
+        if (srcTex && srcTex->target() != GR_GL_TEXTURE_2D) {
+            return false;
+        }
         return true;
     } else {
         return false;
     }
 }
 
-inline bool can_copy_texsubimage(const GrSurface* dst,
-                                 const GrSurface* src,
-                                 const GrGLGpu* gpu) {
+static inline bool can_copy_texsubimage(const GrSurface* dst,
+                                        const GrSurface* src,
+                                        const GrGLGpu* gpu) {
     // Table 3.9 of the ES2 spec indicates the supported formats with CopyTexSubImage
     // and BGRA isn't in the spec. There doesn't appear to be any extension that adds it. Perhaps
     // many drivers would allow it to work, but ANGLE does not.
@@ -2794,18 +2825,29 @@
     if (srcRT && srcRT->renderFBOID() != srcRT->textureFBOID()) {
         return false;
     }
+
+    const GrGLTexture* dstTex = static_cast<const GrGLTexture*>(dst->asTexture());
+    // CopyTex(Sub)Image writes to a texture and we have no way of dynamically wrapping a RT in a
+    // texture.
+    if (!dstTex) {
+        return false;
+    }
+
+    const GrGLTexture* srcTex = static_cast<const GrGLTexture*>(src->asTexture());
+    
+    // Check that we could wrap the source in an FBO, that the dst is TEXTURE_2D, that no mirroring
+    // is required.
     if (gpu->glCaps().isConfigRenderable(src->config(), src->desc().fSampleCnt > 0) &&
-        dst->asTexture() &&
-        dst->origin() == src->origin() &&
-        !GrPixelConfigIsCompressed(src->config())) {
+        !GrPixelConfigIsCompressed(src->config()) &&
+        (!srcTex || srcTex->target() == GR_GL_TEXTURE_2D) &&
+        dstTex->target() == GR_GL_TEXTURE_2D &&
+        dst->origin() == src->origin()) {
         return true;
     } else {
         return false;
     }
 }
 
-}
-
 // If a temporary FBO was created, its non-zero ID is returned. The viewport that the copy rect is
 // relative to is output.
 void GrGLGpu::bindSurfaceFBOForCopy(GrSurface* surface, GrGLenum fboTarget, GrGLIRect* viewport,
@@ -2863,6 +2905,12 @@
         return true;
     }
 
+    const GrGLTexture* srcTexture = static_cast<const GrGLTexture*>(src->asTexture());
+    if (srcTexture && srcTexture->target() != GR_GL_TEXTURE_2D) {
+        // Not supported for FBO blit or CopyTexSubImage
+        return false;
+    }
+
     // We look for opportunities to use CopyTexSubImage, or fbo blit. If neither are
     // possible and we return false to fallback to creating a render target dst for render-to-
     // texture. This code prefers CopyTexSubImage to fbo blit and avoids triggering temporary fbo
@@ -2927,92 +2975,109 @@
 }
 
 
-void GrGLGpu::createCopyProgram() {
-    const char* version = this->glCaps().glslCaps()->versionDeclString();
-
-    GrGLSLShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier);
-    GrGLSLShaderVar uTexCoordXform("u_texCoordXform", kVec4f_GrSLType,
-                                 GrShaderVar::kUniform_TypeModifier);
-    GrGLSLShaderVar uPosXform("u_posXform", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier);
-    GrGLSLShaderVar uTexture("u_texture", kSampler2D_GrSLType, GrShaderVar::kUniform_TypeModifier);
-    GrGLSLShaderVar vTexCoord("v_texCoord", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier);
-    GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier);
-    
-    SkString vshaderTxt(version);
-    aVertex.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
-    vshaderTxt.append(";");
-    uTexCoordXform.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
-    vshaderTxt.append(";");
-    uPosXform.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
-    vshaderTxt.append(";");
-    vTexCoord.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
-    vshaderTxt.append(";");
-    
-    vshaderTxt.append(
-        "// Copy Program VS\n"
-        "void main() {"
-        "  v_texCoord = a_vertex.xy * u_texCoordXform.xy + u_texCoordXform.zw;"
-        "  gl_Position.xy = a_vertex * u_posXform.xy + u_posXform.zw;"
-        "  gl_Position.zw = vec2(0, 1);"
-        "}"
-    );
-
-    SkString fshaderTxt(version);
-    GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision,
-                                                 *this->glCaps().glslCaps(),
-                                                 &fshaderTxt);
-    vTexCoord.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier);
-    vTexCoord.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
-    fshaderTxt.append(";");
-    uTexture.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
-    fshaderTxt.append(";");
-    const char* fsOutName;
-    if (this->glCaps().glslCaps()->mustDeclareFragmentShaderOutput()) {
-        oFragColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
-        fshaderTxt.append(";");
-        fsOutName = oFragColor.c_str();
-    } else {
-        fsOutName = "gl_FragColor";
+void GrGLGpu::createCopyPrograms() {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) {
+        fCopyPrograms[i].fProgram = 0;
     }
-    fshaderTxt.appendf(
-        "// Copy Program FS\n"
-        "void main() {"
-        "  %s = %s(u_texture, v_texCoord);"
-        "}",
-        fsOutName,
-        GrGLSLTexture2DFunctionName(kVec2f_GrSLType, this->glslGeneration())
-    );
+    const char* version = this->glCaps().glslCaps()->versionDeclString();
+    static const GrSLType kSamplerTypes[2] = { kSampler2D_GrSLType, kSamplerExternal_GrSLType };
+    SkASSERT(2 == SK_ARRAY_COUNT(fCopyPrograms));
+    int programCount = this->glCaps().externalTextureSupport() ? 2 : 1;
+    for (int i = 0; i < programCount; ++i) {
+        GrGLSLShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier);
+        GrGLSLShaderVar uTexCoordXform("u_texCoordXform", kVec4f_GrSLType,
+                                     GrShaderVar::kUniform_TypeModifier);
+        GrGLSLShaderVar uPosXform("u_posXform", kVec4f_GrSLType,
+                                  GrShaderVar::kUniform_TypeModifier);
+        GrGLSLShaderVar uTexture("u_texture", kSamplerTypes[i],
+                                 GrShaderVar::kUniform_TypeModifier);
+        GrGLSLShaderVar vTexCoord("v_texCoord", kVec2f_GrSLType,
+                                  GrShaderVar::kVaryingOut_TypeModifier);
+        GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType,
+                                   GrShaderVar::kOut_TypeModifier);
     
-    GL_CALL_RET(fCopyProgram.fProgram, CreateProgram());
-    const char* str;
-    GrGLint length;
+        SkString vshaderTxt(version);
+        aVertex.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
+        vshaderTxt.append(";");
+        uTexCoordXform.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
+        vshaderTxt.append(";");
+        uPosXform.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
+        vshaderTxt.append(";");
+        vTexCoord.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
+        vshaderTxt.append(";");
+    
+        vshaderTxt.append(
+            "// Copy Program VS\n"
+            "void main() {"
+            "  v_texCoord = a_vertex.xy * u_texCoordXform.xy + u_texCoordXform.zw;"
+            "  gl_Position.xy = a_vertex * u_posXform.xy + u_posXform.zw;"
+            "  gl_Position.zw = vec2(0, 1);"
+            "}"
+        );
 
-    str = vshaderTxt.c_str();
-    length = SkToInt(vshaderTxt.size());
-    GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fCopyProgram.fProgram,
-                                                  GR_GL_VERTEX_SHADER, &str, &length, 1, &fStats);
+        SkString fshaderTxt(version);
+        if (kSamplerTypes[i] == kSamplerExternal_GrSLType) {
+            fshaderTxt.appendf("#extension %s : require\n",
+                               this->glCaps().glslCaps()->externalTextureExtensionString());
+        }
+        GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision,
+                                                     *this->glCaps().glslCaps(),
+                                                     &fshaderTxt);
+        vTexCoord.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier);
+        vTexCoord.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
+        fshaderTxt.append(";");
+        uTexture.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
+        fshaderTxt.append(";");
+        const char* fsOutName;
+        if (this->glCaps().glslCaps()->mustDeclareFragmentShaderOutput()) {
+            oFragColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
+            fshaderTxt.append(";");
+            fsOutName = oFragColor.c_str();
+        } else {
+            fsOutName = "gl_FragColor";
+        }
+        fshaderTxt.appendf(
+            "// Copy Program FS\n"
+            "void main() {"
+            "  %s = %s(u_texture, v_texCoord);"
+            "}",
+            fsOutName,
+            GrGLSLTexture2DFunctionName(kVec2f_GrSLType, this->glslGeneration())
+        );
+    
+        GL_CALL_RET(fCopyPrograms[i].fProgram, CreateProgram());
+        const char* str;
+        GrGLint length;
 
-    str = fshaderTxt.c_str();
-    length = SkToInt(fshaderTxt.size());
-    GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fCopyProgram.fProgram,
-                                                  GR_GL_FRAGMENT_SHADER, &str, &length, 1, &fStats);
+        str = vshaderTxt.c_str();
+        length = SkToInt(vshaderTxt.size());
+        GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fCopyPrograms[i].fProgram,
+                                                      GR_GL_VERTEX_SHADER, &str, &length, 1,
+                                                      &fStats);
 
-    GL_CALL(LinkProgram(fCopyProgram.fProgram));
+        str = fshaderTxt.c_str();
+        length = SkToInt(fshaderTxt.size());
+        GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fCopyPrograms[i].fProgram,
+                                                      GR_GL_FRAGMENT_SHADER, &str, &length, 1,
+                                                      &fStats);
 
-    GL_CALL_RET(fCopyProgram.fTextureUniform, GetUniformLocation(fCopyProgram.fProgram,
-                                                                 "u_texture"));
-    GL_CALL_RET(fCopyProgram.fPosXformUniform, GetUniformLocation(fCopyProgram.fProgram,
-                                                                  "u_posXform"));
-    GL_CALL_RET(fCopyProgram.fTexCoordXformUniform, GetUniformLocation(fCopyProgram.fProgram,
-                                                                       "u_texCoordXform"));
+        GL_CALL(LinkProgram(fCopyPrograms[i].fProgram));
 
-    GL_CALL(BindAttribLocation(fCopyProgram.fProgram, 0, "a_vertex"));
+        GL_CALL_RET(fCopyPrograms[i].fTextureUniform,
+                    GetUniformLocation(fCopyPrograms[i].fProgram, "u_texture"));
+        GL_CALL_RET(fCopyPrograms[i].fPosXformUniform,
+                    GetUniformLocation(fCopyPrograms[i].fProgram, "u_posXform"));
+        GL_CALL_RET(fCopyPrograms[i].fTexCoordXformUniform,
+                    GetUniformLocation(fCopyPrograms[i].fProgram, "u_texCoordXform"));
 
-    GL_CALL(DeleteShader(vshader));
-    GL_CALL(DeleteShader(fshader));
+        GL_CALL(BindAttribLocation(fCopyPrograms[i].fProgram, 0, "a_vertex"));
 
-    GL_CALL(GenBuffers(1, &fCopyProgram.fArrayBuffer));
-    fHWGeometryState.setVertexBufferID(this, fCopyProgram.fArrayBuffer);
+        GL_CALL(DeleteShader(vshader));
+        GL_CALL(DeleteShader(fshader));
+    }
+
+    GL_CALL(GenBuffers(1, &fCopyProgramArrayBuffer));
+    fHWGeometryState.setVertexBufferID(this, fCopyProgramArrayBuffer);
     static const GrGLfloat vdata[] = {
         0, 0,
         0, 1,
@@ -3041,14 +3106,16 @@
     SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, w, h);
     this->flushRenderTarget(dstRT, &dstRect);
 
-    GL_CALL(UseProgram(fCopyProgram.fProgram));
-    fHWProgramID = fCopyProgram.fProgram;
+    int progIdx = TextureTargetToCopyProgramIdx(srcTex->target());
+
+    GL_CALL(UseProgram(fCopyPrograms[progIdx].fProgram));
+    fHWProgramID = fCopyPrograms[progIdx].fProgram;
 
     fHWGeometryState.setVertexArrayID(this, 0);
 
     GrGLAttribArrayState* attribs =
-        fHWGeometryState.bindArrayAndBufferToDraw(this, fCopyProgram.fArrayBuffer);
-    attribs->set(this, 0, fCopyProgram.fArrayBuffer, 2, GR_GL_FLOAT, false,
+        fHWGeometryState.bindArrayAndBufferToDraw(this, fCopyProgramArrayBuffer);
+    attribs->set(this, 0, fCopyProgramArrayBuffer, 2, GR_GL_FLOAT, false,
                     2 * sizeof(GrGLfloat), 0);
     attribs->disableUnusedArrays(this, 0x1);
 
@@ -3076,9 +3143,10 @@
         sy1 = 1.f - sy1;
     }
 
-    GL_CALL(Uniform4f(fCopyProgram.fPosXformUniform, dx1 - dx0, dy1 - dy0, dx0, dy0));
-    GL_CALL(Uniform4f(fCopyProgram.fTexCoordXformUniform, sx1 - sx0, sy1 - sy0, sx0, sy0));
-    GL_CALL(Uniform1i(fCopyProgram.fTextureUniform, 0));
+    GL_CALL(Uniform4f(fCopyPrograms[progIdx].fPosXformUniform, dx1 - dx0, dy1 - dy0, dx0, dy0));
+    GL_CALL(Uniform4f(fCopyPrograms[progIdx].fTexCoordXformUniform,
+                      sx1 - sx0, sy1 - sy0, sx0, sy0));
+    GL_CALL(Uniform1i(fCopyPrograms[progIdx].fTextureUniform, 0));
 
     GrXferProcessor::BlendInfo blendInfo;
     blendInfo.reset();
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index c8da150..637a1cf 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -322,7 +322,7 @@
 
     SkAutoTUnref<GrGLContext>  fGLContext;
 
-    void createCopyProgram();
+    void createCopyPrograms();
 
     // GL program-related state
     ProgramCache*               fProgramCache;
@@ -497,8 +497,17 @@
         GrGLint     fTextureUniform;
         GrGLint     fTexCoordXformUniform;
         GrGLint     fPosXformUniform;
-        GrGLuint    fArrayBuffer;
-    } fCopyProgram;
+    }                           fCopyPrograms[2];
+    GrGLuint                    fCopyProgramArrayBuffer;
+
+    static int TextureTargetToCopyProgramIdx(GrGLenum target) {
+        if (target == GR_GL_TEXTURE_2D) {
+            return 0;
+        } else {
+            SkASSERT(target == GR_GL_TEXTURE_EXTERNAL);
+            return 1;
+        }
+    }
 
     TriState fMSAAEnabled;
 
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
index c6c846a..da3c656 100644
--- a/src/gpu/gl/GrGLInterface.cpp
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -727,8 +727,8 @@
     }
 
     if (fExtensions.has("EGL_KHR_image") || fExtensions.has("EGL_KHR_image_base")) {
-        if (nullptr == fFunctions.fCreateImage ||
-            nullptr == fFunctions.fDestroyImage) {
+        if (nullptr == fFunctions.fEGLCreateImage ||
+            nullptr == fFunctions.fEGLDestroyImage) {
             RETURN_FALSE_INTERFACE
         }
     }
diff --git a/src/gpu/gl/GrGLProgramDataManager.cpp b/src/gpu/gl/GrGLProgramDataManager.cpp
index a61e697..f0aba49 100644
--- a/src/gpu/gl/GrGLProgramDataManager.cpp
+++ b/src/gpu/gl/GrGLProgramDataManager.cpp
@@ -62,7 +62,7 @@
 
 void GrGLProgramDataManager::setSampler(UniformHandle u, int texUnit) const {
     const Uniform& uni = fUniforms[u.toIndex()];
-    SkASSERT(uni.fType == kSampler2D_GrSLType);
+    SkASSERT(uni.fType == kSampler2D_GrSLType || uni.fType == kSamplerExternal_GrSLType);
     SkASSERT(GrGLSLShaderVar::kNonArray == uni.fArrayCount);
     // FIXME: We still insert a single sampler uniform for every stage. If the shader does not
     // reference the sampler then the compiler may have optimized it out. Uncomment this assert
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 172aca7..a164661 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -31,11 +31,16 @@
 static uint32_t gen_texture_key(const GrProcessor& proc, const GrGLCaps& caps) {
     uint32_t key = 0;
     int numTextures = proc.numTextures();
+    int shift = 0;
     for (int t = 0; t < numTextures; ++t) {
         const GrTextureAccess& access = proc.textureAccess(t);
         if (swizzle_requires_alpha_remapping(*caps.glslCaps(), access.getTexture()->config())) {
-            key |= 1 << t;
+            key |= 1 << shift;
         }
+        if (GR_GL_TEXTURE_EXTERNAL == static_cast<GrGLTexture*>(access.getTexture())->target()) {
+            key |= 2 << shift;
+        }
+        shift += 2;
     }
     return key;
 }
@@ -48,10 +53,8 @@
  * which must be different for every GrProcessor subclass. It can fail if an effect uses too many
  * textures, transforms, etc, for the space allotted in the meta-key.  NOTE, both FPs and GPs share
  * this function because it is hairy, though FPs do not have attribs, and GPs do not have transforms
- *
- * TODO: A better name for this function  would be "compute" instead of "get".
  */
-static bool get_meta_key(const GrProcessor& proc,
+static bool gen_meta_key(const GrProcessor& proc,
                          const GrGLCaps& caps,
                          uint32_t transformKey,
                          GrProcessorKeyBuilder* b) {
@@ -75,15 +78,12 @@
     return true;
 }
 
-/*
- * TODO: A better name for this function  would be "compute" instead of "get".
- */
-static bool get_frag_proc_and_meta_keys(const GrPrimitiveProcessor& primProc,
+static bool gen_frag_proc_and_meta_keys(const GrPrimitiveProcessor& primProc,
                                         const GrFragmentProcessor& fp,
                                         const GrGLCaps& caps,
                                         GrProcessorKeyBuilder* b) {
     for (int i = 0; i < fp.numChildProcessors(); ++i) {
-        if (!get_frag_proc_and_meta_keys(primProc, fp.childProcessor(i), caps, b)) {
+        if (!gen_frag_proc_and_meta_keys(primProc, fp.childProcessor(i), caps, b)) {
             return false;
         }
     }
@@ -91,7 +91,7 @@
     fp.getGLSLProcessorKey(*caps.glslCaps(), b);
 
     //**** use glslCaps here?
-    return get_meta_key(fp, caps, primProc.getTransformKey(fp.coordTransforms(),
+    return gen_meta_key(fp, caps, primProc.getTransformKey(fp.coordTransforms(),
                                                            fp.numTransformsExclChildren()), b);
 }
 
@@ -115,14 +115,14 @@
 
     primProc.getGLSLProcessorKey(*gpu->glCaps().glslCaps(), &b);
     //**** use glslCaps here?
-    if (!get_meta_key(primProc, gpu->glCaps(), 0, &b)) {
+    if (!gen_meta_key(primProc, gpu->glCaps(), 0, &b)) {
         glDesc->key().reset();
         return false;
     }
 
     for (int i = 0; i < pipeline.numFragmentProcessors(); ++i) {
         const GrFragmentProcessor& fp = pipeline.getFragmentProcessor(i);
-        if (!get_frag_proc_and_meta_keys(primProc, fp, gpu->glCaps(), &b)) {
+        if (!gen_frag_proc_and_meta_keys(primProc, fp, gpu->glCaps(), &b)) {
             glDesc->key().reset();
             return false;
         }
@@ -131,7 +131,7 @@
     const GrXferProcessor& xp = *pipeline.getXferProcessor();
     xp.getGLSLProcessorKey(*gpu->glCaps().glslCaps(), &b);
     //**** use glslCaps here?
-    if (!get_meta_key(xp, gpu->glCaps(), 0, &b)) {
+    if (!gen_meta_key(xp, gpu->glCaps(), 0, &b)) {
         glDesc->key().reset();
         return false;
     }
diff --git a/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
index 834e122..05afb2c 100644
--- a/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
+++ b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
@@ -12,8 +12,20 @@
 
 #include <EGL/egl.h>
 
+namespace {
+struct Libs {
+    void* fGLLib;
+    void* fEGLLib;
+};
+}
+
 static GrGLFuncPtr angle_get_gl_proc(void* ctx, const char name[]) {
-    GrGLFuncPtr proc = (GrGLFuncPtr) GetProcedureAddress(ctx, name);
+    const Libs* libs = reinterpret_cast<const Libs*>(ctx);
+    GrGLFuncPtr proc = (GrGLFuncPtr) GetProcedureAddress(libs->fGLLib, name);
+    if (proc) {
+        return proc;
+    }
+    proc = (GrGLFuncPtr) GetProcedureAddress(libs->fEGLLib, name);
     if (proc) {
         return proc;
     }
@@ -21,23 +33,26 @@
 }
 
 const GrGLInterface* GrGLCreateANGLEInterface() {
-    static void* gANGLELib = nullptr;
+    static Libs gLibs = { nullptr, nullptr };
 
-    if (nullptr == gANGLELib) {
+    if (nullptr == gLibs.fGLLib) {
         // We load the ANGLE library and never let it go
 #if defined _WIN32
-        gANGLELib = DynamicLoadLibrary("libGLESv2.dll");
+        gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dll");
+        gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dll");
 #elif defined SK_BUILD_FOR_MAC
-        gANGLELib = DynamicLoadLibrary("libGLESv2.dylib");
+        gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dylib");
+        gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dylib");
 #else
-        gANGLELib = DynamicLoadLibrary("libGLESv2.so");
-#endif // defined _WIN32
+        gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.so");
+        gLibs.fGLLib = DynamicLoadLibrary("libEGL.so");
+#endif
     }
 
-    if (nullptr == gANGLELib) {
+    if (nullptr == gLibs.fGLLib || nullptr == gLibs.fEGLLib) {
         // We can't setup the interface correctly w/o the so
         return nullptr;
     }
 
-    return GrGLAssembleGLESInterface(gANGLELib, angle_get_gl_proc);
+    return GrGLAssembleGLESInterface(&gLibs, angle_get_gl_proc);
 }
diff --git a/src/gpu/gl/angle/SkANGLEGLContext.cpp b/src/gpu/gl/angle/SkANGLEGLContext.cpp
index 64a3bde..54ef02d 100644
--- a/src/gpu/gl/angle/SkANGLEGLContext.cpp
+++ b/src/gpu/gl/angle/SkANGLEGLContext.cpp
@@ -11,6 +11,9 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
+#include "gl/GrGLDefines.h"
+#include "gl/GrGLUtil.h"
+
 #define EGL_PLATFORM_ANGLE_ANGLE                0x3202
 #define EGL_PLATFORM_ANGLE_TYPE_ANGLE           0x3203
 #define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE      0x3207
@@ -34,8 +37,7 @@
               EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE,
               EGL_NONE
         };
-        display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
-                                            nativeDisplay, attribs);
+        display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, nativeDisplay, attribs);
     } else {
         // Try for an ANGLE D3D11 context, fall back to D3D9, and finally GL.
         EGLint attribs[3][3] = {
@@ -56,8 +58,7 @@
             }
         };
         for (int i = 0; i < 3 && display == EGL_NO_DISPLAY; ++i) {
-            display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
-                                                nativeDisplay, attribs[i]);
+            display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,nativeDisplay, attribs[i]);
         }
     }
     return display;
@@ -79,6 +80,7 @@
         EGL_NONE
     };
 
+    fIsGLBackend = useGLBackend;
     fDisplay = GetD3DEGLDisplay(EGL_DEFAULT_DISPLAY, useGLBackend);
     if (EGL_NO_DISPLAY == fDisplay) {
         SkDebugf("Could not create EGL display!");
@@ -129,6 +131,62 @@
     this->destroyGLContext();
 }
 
+GrEGLImage SkANGLEGLContext::texture2DToEGLImage(GrGLuint texID) const {
+    if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
+        return GR_EGL_NO_IMAGE;
+    }
+    GrEGLImage img;
+    GrEGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0,
+                           GR_EGL_IMAGE_PRESERVED, GR_EGL_TRUE,
+                           GR_EGL_NONE };
+    // 64 bit cast is to shut Visual C++ up about casting 32 bit value to a pointer.
+    GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>((uint64_t)texID);
+    GR_GL_CALL_RET(this->gl(), img,
+                   EGLCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer,
+                                  attribs));
+    return img;
+}
+
+void SkANGLEGLContext::destroyEGLImage(GrEGLImage image) const {
+    GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image));
+}
+
+GrGLuint SkANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const {
+    GrGLClearErr(this->gl());
+    if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
+        return 0;
+    }
+    GrGLEGLImageTargetTexture2DProc glEGLImageTargetTexture2D =
+        (GrGLEGLImageTargetTexture2DProc)eglGetProcAddress("glEGLImageTargetTexture2DOES");
+    if (!glEGLImageTargetTexture2D) {
+        return 0;
+    }
+    GrGLuint texID;
+    GR_GL_CALL(this->gl(), GenTextures(1, &texID));
+    if (!texID) {
+        return 0;
+    }
+    GR_GL_CALL(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
+    if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
+        GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
+        return 0;
+    }
+    glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
+    if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
+        GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
+        return 0;
+    }
+    return texID;
+}
+
+SkGLContext* SkANGLEGLContext::createNew() const {
+    SkGLContext* ctx = SkANGLEGLContext::Create(this->gl()->fStandard, fIsGLBackend);
+    if (ctx) {
+        ctx->makeCurrent();
+    }
+    return ctx;
+}
+
 void SkANGLEGLContext::destroyGLContext() {
     if (fDisplay) {
         eglMakeCurrent(fDisplay, 0, 0, 0);
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 0ae4f96..b5108a7 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -319,6 +319,16 @@
     SkASSERT(fFS.hasReadFragmentPosition() == fp.willReadFragmentPosition());
 }
 
+static GrSLType get_sampler_type(const GrTextureAccess& access) {
+    GrGLTexture* glTexture = static_cast<GrGLTexture*>(access.getTexture());
+    if (glTexture->target() == GR_GL_TEXTURE_EXTERNAL) {
+        return kSamplerExternal_GrSLType;
+    } else {
+        SkASSERT(glTexture->target() == GR_GL_TEXTURE_2D);
+        return kSampler2D_GrSLType;
+    }
+}
+
 template <class Proc>
 void GrGLProgramBuilder::emitSamplers(const GrProcessor& processor,
                                       GrGLSLTextureSampler::TextureSamplerArray* outSamplers,
@@ -329,11 +339,19 @@
     SkString name;
     for (int t = 0; t < numTextures; ++t) {
         name.printf("Sampler%d", t);
+        GrSLType samplerType = get_sampler_type(processor.textureAccess(t));
         localSamplerUniforms[t] = this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                                                   kSampler2D_GrSLType, kDefault_GrSLPrecision,
+                                                   samplerType, kDefault_GrSLPrecision,
                                                    name.c_str());
         SkNEW_APPEND_TO_TARRAY(outSamplers, GrGLSLTextureSampler,
                                (localSamplerUniforms[t], processor.textureAccess(t)));
+        if (kSamplerExternal_GrSLType == samplerType) {
+            const char* externalFeatureString = this->glslCaps()->externalTextureExtensionString();
+            // We shouldn't ever create a GrGLTexture that requires external sampler type 
+            SkASSERT(externalFeatureString);
+            fFS.addFeature(1 << GrGLSLFragmentShaderBuilder::kExternalTexture_GLSLPrivateFeature,
+                           externalFeatureString);
+        }
     }
 }
 
diff --git a/src/gpu/gl/egl/GrGLCreateNativeInterface_egl.cpp b/src/gpu/gl/egl/GrGLCreateNativeInterface_egl.cpp
index 703e39d..fef4f02 100644
--- a/src/gpu/gl/egl/GrGLCreateNativeInterface_egl.cpp
+++ b/src/gpu/gl/egl/GrGLCreateNativeInterface_egl.cpp
@@ -14,7 +14,15 @@
 
 static GrGLFuncPtr egl_get_gl_proc(void* ctx, const char name[]) {
     SkASSERT(nullptr == ctx);
-    return eglGetProcAddress(name);
+    GrGLFuncPtr ptr = eglGetProcAddress(name);
+    if (!ptr) {
+        if (0 == strcmp("eglQueryString", name)) {
+            return (GrGLFuncPtr)eglQueryString;
+        } else if (0 == strcmp("eglGetCurrentDisplay", name)) {
+            return (GrGLFuncPtr)eglGetCurrentDisplay;
+        }
+    }
+    return ptr;
 }
 
 const GrGLInterface* GrGLCreateNativeInterface() {
diff --git a/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp b/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp
index d1335d3..591cae3 100644
--- a/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp
+++ b/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp
@@ -13,6 +13,9 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
+#include "gl/GrGLDefines.h"
+#include "gl/GrGLUtil.h"
+
 namespace {
 
 // TODO: Share this class with ANGLE if/when it gets support for EGL_KHR_fence_sync.
@@ -37,6 +40,11 @@
     EGLGLContext(GrGLStandard forcedGpuAPI);
     ~EGLGLContext() override;
 
+    GrEGLImage texture2DToEGLImage(GrGLuint texID) const override;
+    void destroyEGLImage(GrEGLImage) const override;
+    GrGLuint eglImageToExternalTexture(GrEGLImage) const override;
+    SkGLContext* createNew() const override;
+
 private:
     void destroyGLContext();
 
@@ -200,6 +208,57 @@
     }
 }
 
+GrEGLImage EGLGLContext::texture2DToEGLImage(GrGLuint texID) const {
+    if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
+        return GR_EGL_NO_IMAGE;
+    }
+    GrEGLImage img;
+    GrEGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0, GR_EGL_NONE };
+    GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>(texID);
+    GR_GL_CALL_RET(this->gl(), img,
+                   EGLCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, attribs));
+    return img;
+}
+
+void EGLGLContext::destroyEGLImage(GrEGLImage image) const {
+    GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image));
+}
+
+GrGLuint EGLGLContext::eglImageToExternalTexture(GrEGLImage image) const {
+    GrGLClearErr(this->gl());
+    if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
+        return 0;
+    }
+    GrGLEGLImageTargetTexture2DProc glEGLImageTargetTexture2D = 
+            (GrGLEGLImageTargetTexture2DProc) eglGetProcAddress("glEGLImageTargetTexture2DOES");
+    if (!glEGLImageTargetTexture2D) {
+        return 0;
+    }
+    GrGLuint texID;
+    glGenTextures(1, &texID);
+    if (!texID) {
+        return 0;
+    }
+    glBindTexture(GR_GL_TEXTURE_EXTERNAL, texID);
+    if (glGetError() != GR_GL_NO_ERROR) {
+        glDeleteTextures(1, &texID);
+        return 0;
+    }
+    glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
+    if (glGetError() != GR_GL_NO_ERROR) {
+        glDeleteTextures(1, &texID);
+        return 0;
+    }
+    return texID;
+}
+
+SkGLContext* EGLGLContext::createNew() const {
+    SkGLContext* ctx = SkCreatePlatformGLContext(this->gl()->fStandard);
+    if (ctx) {
+        ctx->makeCurrent();
+    }
+    return ctx;
+}
 
 void EGLGLContext::onPlatformMakeCurrent() const {
     if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
diff --git a/src/gpu/glsl/GrGLSL.h b/src/gpu/glsl/GrGLSL.h
index a39f104..ac38522 100644
--- a/src/gpu/glsl/GrGLSL.h
+++ b/src/gpu/glsl/GrGLSL.h
@@ -85,6 +85,8 @@
             return "mat4";
         case kSampler2D_GrSLType:
             return "sampler2D";
+        case kSamplerExternal_GrSLType:
+            return "samplerExternalOES";
         default:
             SkFAIL("Unknown shader var type.");
             return ""; // suppress warning
diff --git a/src/gpu/glsl/GrGLSLCaps.cpp b/src/gpu/glsl/GrGLSLCaps.cpp
index aed9888..191fb56 100755
--- a/src/gpu/glsl/GrGLSLCaps.cpp
+++ b/src/gpu/glsl/GrGLSLCaps.cpp
@@ -28,6 +28,7 @@
     fShaderDerivativeExtensionString = nullptr;
     fFragCoordConventionsExtensionString = nullptr;
     fSecondaryOutputExtensionString = nullptr;
+    fExternalTextureExtensionString = nullptr;
     fFBFetchColorName = nullptr;
     fFBFetchExtensionString = nullptr;
     fAdvBlendEqInteraction = kNotSupported_AdvBlendEqInteraction;
diff --git a/src/gpu/glsl/GrGLSLCaps.h b/src/gpu/glsl/GrGLSLCaps.h
index 68e91b6..9924773 100755
--- a/src/gpu/glsl/GrGLSLCaps.h
+++ b/src/gpu/glsl/GrGLSLCaps.h
@@ -102,6 +102,10 @@
         return fSecondaryOutputExtensionString;
     }
 
+    const char* externalTextureExtensionString() const {
+        return fExternalTextureExtensionString;
+    }
+
     bool mustSwizzleInShader() const { return fMustSwizzleInShader; }
 
     /**
@@ -140,6 +144,7 @@
     const char* fShaderDerivativeExtensionString;
     const char* fFragCoordConventionsExtensionString;
     const char* fSecondaryOutputExtensionString;
+    const char* fExternalTextureExtensionString;
 
     const char* fFBFetchColorName;
     const char* fFBFetchExtensionString;
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
index 924e311..a55fdd2 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
@@ -172,6 +172,7 @@
         kFragCoordConventions_GLSLPrivateFeature = kLastGLSLFeature + 1,
         kBlendEquationAdvanced_GLSLPrivateFeature,
         kBlendFuncExtended_GLSLPrivateFeature,
+        kExternalTexture_GLSLPrivateFeature,
         kLastGLSLPrivateFeature = kBlendFuncExtended_GLSLPrivateFeature
     };
 
diff --git a/tests/EGLImageTest.cpp b/tests/EGLImageTest.cpp
new file mode 100644
index 0000000..088049e
--- /dev/null
+++ b/tests/EGLImageTest.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrContextFactory.h"
+#include "gl/GrGLGpu.h"
+#include "gl/GrGLUtil.h"
+#include "gl/SkGLContext.h"
+
+static void cleanup(SkGLContext* glctx0, GrGLuint texID0, SkGLContext* glctx1, GrContext* grctx1,
+                    const GrGLTextureInfo* grbackendtex1, GrEGLImage image1) {
+    if (glctx1) {
+        glctx1->makeCurrent();
+        if (grctx1) {
+            if (grbackendtex1) {
+                GrGLGpu* gpu1 = static_cast<GrGLGpu*>(grctx1->getGpu());
+                GrBackendObject handle = reinterpret_cast<GrBackendObject>(grbackendtex1);
+                gpu1->deleteTestingOnlyBackendTexture(handle, false);
+            }
+            grctx1->unref();
+        }
+        if (GR_EGL_NO_IMAGE != image1) {
+            glctx1->destroyEGLImage(image1);
+        }
+        glctx1->unref();
+    }
+
+    glctx0->makeCurrent();
+    if (texID0) {
+        GR_GL_CALL(glctx0->gl(), DeleteTextures(1, &texID0));
+    }
+}
+
+DEF_GPUTEST(EGLImageTest, reporter, factory) {
+    for (int glCtxType = 0; glCtxType < GrContextFactory::kGLContextTypeCnt; ++glCtxType) {
+        GrContextFactory::GLContextType type = (GrContextFactory::GLContextType)glCtxType;
+        if (!GrContextFactory::IsRenderingGLContext(type)) {
+            continue;
+        }
+        
+        // Try to create a second GL context and then check if the contexts have necessary
+        // extensions to run this test.
+
+        GrContext* context0 = factory->get(type);
+        if (!context0) {
+            continue;
+        }
+        SkGLContext* glCtx0 = factory->getGLContext(type);
+        SkGLContext* glCtx1 = glCtx0->createNew();
+        if (!glCtx1) {
+            continue;
+        }
+        GrContext* context1 = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)glCtx1->gl());
+        const GrGLTextureInfo* backendTexture1 = nullptr;
+        GrEGLImage image = GR_EGL_NO_IMAGE;
+        GrGLTextureInfo externalTexture;
+        externalTexture.fID = 0;
+
+        if (!context1) {
+            cleanup(glCtx0, externalTexture.fID, glCtx1, context1, backendTexture1, image);
+            continue;
+        }
+
+        SkASSERT(glCtx0);
+        if (!glCtx0->gl()->hasExtension("EGL_KHR_image") ||
+            !glCtx0->gl()->hasExtension("GL_OES_EGL_image_external") ||
+            !glCtx1->gl()->hasExtension("EGL_KHR_image") ||
+            !glCtx1->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
+            cleanup(glCtx0, externalTexture.fID, glCtx1, context1, backendTexture1, image);
+            continue;
+        }
+
+///////////////////////////////// CONTEXT 1 ///////////////////////////////////
+
+        // Use GL Context 1 to create a texture unknown to GrContext.
+        context1->flush();
+        GrGpu* gpu1 = context1->getGpu();
+        static const int kSize = 100;
+        backendTexture1 = reinterpret_cast<const GrGLTextureInfo*>(
+            gpu1->createTestingOnlyBackendTexture(nullptr, kSize, kSize, kRGBA_8888_GrPixelConfig));
+        if (!backendTexture1 || !backendTexture1->fID) {
+            ERRORF(reporter, "Error creating texture for EGL Image");
+            cleanup(glCtx0, externalTexture.fID, glCtx1, context1, backendTexture1, image);
+            continue;
+        }
+        if (GR_GL_TEXTURE_2D != backendTexture1->fTarget) {
+            ERRORF(reporter, "Expected backend texture to be 2D");
+            cleanup(glCtx0, externalTexture.fID, glCtx1, context1, backendTexture1, image);
+            continue;
+        }
+
+        // Wrap the texture in an EGLImage
+        image = glCtx1->texture2DToEGLImage(backendTexture1->fID);
+        if (GR_EGL_NO_IMAGE == image) {
+            ERRORF(reporter, "Error creating EGL Image from texture");
+            cleanup(glCtx0, externalTexture.fID, glCtx1, context1, backendTexture1, image);
+            continue;
+        }
+
+        // Populate the texture using GL context 1. Important to use TexSubImage as TexImage orphans
+        // the EGL image. Also, this must be done after creating the EGLImage as the texture
+        // contents may not be preserved when the image is created.
+        SkAutoTMalloc<uint32_t> pixels(kSize * kSize);
+        for (int i = 0; i < kSize*kSize; ++i) {
+            pixels.get()[i] = 0xDDAABBCC;
+        }
+        GR_GL_CALL(glCtx1->gl(), ActiveTexture(GR_GL_TEXTURE0));
+        GR_GL_CALL(glCtx1->gl(), BindTexture(backendTexture1->fTarget, backendTexture1->fID));
+        GR_GL_CALL(glCtx1->gl(), TexSubImage2D(backendTexture1->fTarget, 0, 0, 0, kSize, kSize,
+                                               GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, pixels.get()));
+        GR_GL_CALL(glCtx1->gl(), Finish());
+        // We've been making direct GL calls in GL context 1, let GrContext 1 know its internal
+        // state is invalid.
+        context1->resetContext();        
+
+///////////////////////////////// CONTEXT 0 ///////////////////////////////////
+
+        // Make a new texture ID in GL Context 0 from the EGL Image
+        glCtx0->makeCurrent();
+        externalTexture.fTarget = GR_GL_TEXTURE_EXTERNAL;
+        externalTexture.fID = glCtx0->eglImageToExternalTexture(image);
+
+        // Wrap this texture ID in a GrTexture
+        GrBackendTextureDesc externalDesc;
+        externalDesc.fConfig = kRGBA_8888_GrPixelConfig;
+        externalDesc.fWidth = kSize;
+        externalDesc.fHeight = kSize;
+        externalDesc.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
+        SkAutoTUnref<GrTexture> externalTextureObj(
+            context0->textureProvider()->wrapBackendTexture(externalDesc));
+        if (!externalTextureObj) {
+            ERRORF(reporter, "Error wrapping external texture in GrTexture.");
+            cleanup(glCtx0, externalTexture.fID, glCtx1, context1, backendTexture1, image);
+            continue;
+        }
+
+        // Read the pixels and see if we get the values set in GL context 1
+        memset(pixels.get(), 0, sizeof(uint32_t)*kSize*kSize);
+        bool read = externalTextureObj->readPixels(0, 0, kSize, kSize, kRGBA_8888_GrPixelConfig,
+                                                   pixels.get());
+        if (!read) {
+            ERRORF(reporter, "Error reading external texture.");
+            cleanup(glCtx0, externalTexture.fID, glCtx1, context1, backendTexture1, image);
+            continue;
+        }
+        for (int i = 0; i < kSize*kSize; ++i) {
+            if (pixels.get()[i] != 0xDDAABBCC) {
+                ERRORF(reporter, "Error, external texture pixel value %d should be 0xDDAABBCC,"
+                                 " got 0x%08x.", pixels.get()[i]);
+                break;
+            }
+        }
+        cleanup(glCtx0, externalTexture.fID, glCtx1, context1, backendTexture1, image);
+    }
+}
+
+#endif