Implement the egl and gl layers of EGL Image.

Add end2end tests and unittests.

BUG=angleproject:970

Change-Id: I13fc501b24c3f11bfedc810c1ff80fcf1318877c
Reviewed-on: https://chromium-review.googlesource.com/287343
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Tested-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Texture.cpp b/src/libANGLE/Texture.cpp
index 8b18663..2668478 100644
--- a/src/libANGLE/Texture.cpp
+++ b/src/libANGLE/Texture.cpp
@@ -12,6 +12,7 @@
 #include "common/utilities.h"
 #include "libANGLE/Config.h"
 #include "libANGLE/Data.h"
+#include "libANGLE/Image.h"
 #include "libANGLE/Surface.h"
 #include "libANGLE/formatutils.h"
 
@@ -46,7 +47,7 @@
 }
 
 Texture::Texture(rx::TextureImpl *impl, GLuint id, GLenum target)
-    : FramebufferAttachmentObject(id),
+    : egl::ImageSibling(id),
       mTexture(impl),
       mUsage(GL_NONE),
       mImmutableLevelCount(0),
@@ -127,6 +128,11 @@
     return mCompletenessCache.samplerComplete;
 }
 
+bool Texture::isMipmapComplete() const
+{
+    return computeMipmapCompleteness(mSamplerState);
+}
+
 // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
 bool Texture::isCubeComplete() const
 {
@@ -152,6 +158,22 @@
     return true;
 }
 
+size_t Texture::getMipCompleteLevels() const
+{
+    const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), 0);
+    if (mTarget == GL_TEXTURE_3D)
+    {
+        const size_t maxDim =
+            std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height),
+                     baseImageDesc.size.depth);
+        return log2(maxDim) + 1;
+    }
+    else
+    {
+        return log2(std::max(baseImageDesc.size.width, baseImageDesc.size.height)) + 1;
+    }
+}
+
 bool Texture::isImmutable() const
 {
     return (mImmutableLevelCount > 0);
@@ -162,6 +184,11 @@
     return mImmutableLevelCount;
 }
 
+egl::Surface *Texture::getBoundSurface() const
+{
+    return mBoundSurface;
+}
+
 Error Texture::setImage(GLenum target, size_t level, GLenum internalFormat, const Extents &size, GLenum format, GLenum type,
                         const PixelUnpackState &unpack, const uint8_t *pixels)
 {
@@ -169,6 +196,7 @@
 
     // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
     releaseTexImageInternal();
+    orphanImages();
 
     Error error = mTexture->setImage(target, level, internalFormat, size, format, type, unpack, pixels);
     if (error.isError())
@@ -196,6 +224,7 @@
 
     // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
     releaseTexImageInternal();
+    orphanImages();
 
     Error error = mTexture->setCompressedImage(target, level, internalFormat, size, unpack, imageSize, pixels);
     if (error.isError())
@@ -223,6 +252,7 @@
 
     // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
     releaseTexImageInternal();
+    orphanImages();
 
     Error error = mTexture->copyImage(target, level, sourceArea, internalFormat, source);
     if (error.isError())
@@ -250,6 +280,7 @@
 
     // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
     releaseTexImageInternal();
+    orphanImages();
 
     Error error = mTexture->setStorage(target, levels, internalFormat, size);
     if (error.isError())
@@ -270,6 +301,13 @@
     // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
     releaseTexImageInternal();
 
+    // EGL_KHR_gl_image states that images are only orphaned when generating mipmaps if the texture
+    // is not mip complete.
+    if (!isMipmapComplete())
+    {
+        orphanImages();
+    }
+
     Error error = mTexture->generateMipmaps(getSamplerState());
     if (error.isError())
     {
@@ -388,24 +426,38 @@
     }
 }
 
+Error Texture::setEGLImageTarget(GLenum target, egl::Image *imageTarget)
+{
+    ASSERT(target == mTarget);
+    ASSERT(target == GL_TEXTURE_2D);
+
+    // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
+    releaseTexImageInternal();
+    orphanImages();
+
+    Error error = mTexture->setEGLImageTarget(target, imageTarget);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    setTargetImage(imageTarget);
+
+    Extents size(imageTarget->getWidth(), imageTarget->getHeight(), 1);
+    GLenum internalFormat = imageTarget->getInternalFormat();
+    GLenum type           = GetInternalFormatInfo(internalFormat).type;
+
+    clearImageDescs();
+    setImageDesc(target, 0, ImageDesc(size, GetSizedInternalFormat(internalFormat, type)));
+
+    return Error(GL_NO_ERROR);
+}
+
 GLenum Texture::getBaseImageTarget() const
 {
     return mTarget == GL_TEXTURE_CUBE_MAP ? FirstCubeMapTextureTarget : mTarget;
 }
 
-size_t Texture::getExpectedMipLevels() const
-{
-    const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), 0);
-    if (mTarget == GL_TEXTURE_3D)
-    {
-        return log2(std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height), baseImageDesc.size.depth)) + 1;
-    }
-    else
-    {
-        return log2(std::max(baseImageDesc.size.width, baseImageDesc.size.height)) + 1;
-    }
-}
-
 bool Texture::computeSamplerCompleteness(const SamplerState &samplerState, const Data &data) const
 {
     const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), samplerState.baseLevel);
@@ -481,7 +533,7 @@
 
 bool Texture::computeMipmapCompleteness(const gl::SamplerState &samplerState) const
 {
-    size_t expectedMipLevels = getExpectedMipLevels();
+    size_t expectedMipLevels = getMipCompleteLevels();
 
     size_t maxLevel = std::min<size_t>(expectedMipLevels, samplerState.maxLevel + 1);
 
@@ -525,13 +577,13 @@
         return false;
     }
 
-    // The base image level is complete if the width and height are positive
-    if (level == 0)
+    const ImageDesc &levelImageDesc = getImageDesc(target, level);
+    if (levelImageDesc.size.width == 0 || levelImageDesc.size.height == 0 ||
+        levelImageDesc.size.depth == 0)
     {
-        return true;
+        return false;
     }
 
-    const ImageDesc &levelImageDesc = getImageDesc(target, level);
     if (levelImageDesc.internalFormat != baseImageDesc.internalFormat)
     {
         return false;