Release egl::Surface on Texture image changes.

We would get into a broken state if the user would bind a Texture
to an egl::Surface, then change the Texture. This is valid in egl,
and should release the Surface when it happens.

BUG=485543

Change-Id: Idfaa305ac704f2bc579e79be816e88a23e69351b
Reviewed-on: https://chromium-review.googlesource.com/271986
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/280906
diff --git a/src/libANGLE/Surface.cpp b/src/libANGLE/Surface.cpp
index ac455f3..cd06a09 100644
--- a/src/libANGLE/Surface.cpp
+++ b/src/libANGLE/Surface.cpp
@@ -33,8 +33,7 @@
       // FIXME: Determine actual pixel aspect ratio
       mPixelAspectRatio(static_cast<EGLint>(1.0 * EGL_DISPLAY_SCALING)),
       mRenderBuffer(EGL_BACK_BUFFER),
-      mSwapBehavior(EGL_BUFFER_PRESERVED),
-      mTexture(NULL)
+      mSwapBehavior(EGL_BUFFER_PRESERVED)
 {
     addRef();
 
@@ -56,14 +55,14 @@
 
 Surface::~Surface()
 {
-    if (mTexture)
+    if (mTexture.get())
     {
         if (mImplementation)
         {
             mImplementation->releaseTexImage(mTexture->id());
         }
-        mTexture->releaseTexImage();
-        mTexture = NULL;
+        mTexture->releaseTexImageFromSurface();
+        mTexture.set(nullptr);
     }
 
     SafeDelete(mImplementation);
@@ -146,21 +145,26 @@
 
 Error Surface::bindTexImage(gl::Texture *texture, EGLint buffer)
 {
-    ASSERT(!mTexture);
+    ASSERT(!mTexture.get());
 
-    texture->bindTexImage(this);
-    mTexture = texture;
+    texture->bindTexImageFromSurface(this);
+    mTexture.set(texture);
     return mImplementation->bindTexImage(buffer);
 }
 
 Error Surface::releaseTexImage(EGLint buffer)
 {
-    ASSERT(mTexture);
-    gl::Texture *boundTexture = mTexture;
-    mTexture = NULL;
+    ASSERT(mTexture.get());
+    mTexture->releaseTexImageFromSurface();
+    mTexture.set(nullptr);
 
-    boundTexture->releaseTexImage();
     return mImplementation->releaseTexImage(buffer);
 }
 
+void Surface::releaseTexImageFromTexture()
+{
+    ASSERT(mTexture.get());
+    mTexture.set(nullptr);
+}
+
 }
diff --git a/src/libANGLE/Surface.h b/src/libANGLE/Surface.h
index 430bf01..9b42b52 100644
--- a/src/libANGLE/Surface.h
+++ b/src/libANGLE/Surface.h
@@ -64,13 +64,17 @@
     EGLenum getTextureFormat() const;
     EGLenum getTextureTarget() const;
 
-    gl::Texture *getBoundTexture() const { return mTexture; }
+    gl::Texture *getBoundTexture() const { return mTexture.get(); }
 
     EGLint isFixedSize() const;
 
   private:
     virtual ~Surface();
 
+    // ANGLE-only method, used internally
+    friend class gl::Texture;
+    void releaseTexImageFromTexture();
+
     rx::SurfaceImpl *mImplementation;
 
     EGLint mType;
@@ -90,7 +94,7 @@
     EGLenum mRenderBuffer;         // Render buffer
     EGLenum mSwapBehavior;         // Buffer swap behavior
 
-    gl::Texture *mTexture;
+    BindingPointer<gl::Texture> mTexture;
 };
 
 }
diff --git a/src/libANGLE/Texture.cpp b/src/libANGLE/Texture.cpp
index 2d68bec..3f7c79e 100644
--- a/src/libANGLE/Texture.cpp
+++ b/src/libANGLE/Texture.cpp
@@ -181,14 +181,15 @@
 {
     ASSERT(target == mTarget || (mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
 
+    // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
+    releaseTexImageInternal();
+
     Error error = mTexture->setImage(target, level, internalFormat, size, format, type, unpack, pixels);
     if (error.isError())
     {
         return error;
     }
 
-    releaseTexImage();
-
     setImageDesc(target, level, ImageDesc(size, GetSizedInternalFormat(internalFormat, type)));
 
     return Error(GL_NO_ERROR);
@@ -207,14 +208,15 @@
 {
     ASSERT(target == mTarget || (mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
 
+    // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
+    releaseTexImageInternal();
+
     Error error = mTexture->setCompressedImage(target, level, internalFormat, size, unpack, pixels);
     if (error.isError())
     {
         return error;
     }
 
-    releaseTexImage();
-
     setImageDesc(target, level, ImageDesc(size, GetSizedInternalFormat(internalFormat, GL_UNSIGNED_BYTE)));
 
     return Error(GL_NO_ERROR);
@@ -233,14 +235,15 @@
 {
     ASSERT(target == mTarget || (mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
 
+    // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
+    releaseTexImageInternal();
+
     Error error = mTexture->copyImage(target, level, sourceArea, internalFormat, source);
     if (error.isError())
     {
         return error;
     }
 
-    releaseTexImage();
-
     setImageDesc(target, level, ImageDesc(Extents(sourceArea.width, sourceArea.height, 1),
                                           GetSizedInternalFormat(internalFormat, GL_UNSIGNED_BYTE)));
 
@@ -259,14 +262,15 @@
 {
     ASSERT(target == mTarget);
 
+    // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
+    releaseTexImageInternal();
+
     Error error = mTexture->setStorage(target, levels, internalFormat, size);
     if (error.isError())
     {
         return error;
     }
 
-    releaseTexImage();
-
     mImmutableLevelCount = levels;
     clearImageDescs();
     setImageDescChain(levels, size, internalFormat);
@@ -277,14 +281,15 @@
 
 Error Texture::generateMipmaps()
 {
+    // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
+    releaseTexImageInternal();
+
     Error error = mTexture->generateMipmaps();
     if (error.isError())
     {
         return error;
     }
 
-    releaseTexImage();
-
     const ImageDesc &baseImageInfo = getImageDesc(getBaseImageTarget(), 0);
     size_t mipLevels = log2(std::max(std::max(baseImageInfo.size.width, baseImageInfo.size.height), baseImageInfo.size.depth)) + 1;
     setImageDescChain(mipLevels, baseImageInfo.size, baseImageInfo.internalFormat);
@@ -355,11 +360,15 @@
     mCompletenessCache.cacheValid = false;
 }
 
-void Texture::bindTexImage(egl::Surface *surface)
+void Texture::bindTexImageFromSurface(egl::Surface *surface)
 {
     ASSERT(surface);
 
-    releaseTexImage();
+    if (mBoundSurface)
+    {
+        releaseTexImageFromSurface();
+    }
+
     mTexture->bindTexImage(surface);
     mBoundSurface = surface;
 
@@ -370,16 +379,26 @@
     setImageDesc(mTarget, 0, desc);
 }
 
-void Texture::releaseTexImage()
+void Texture::releaseTexImageFromSurface()
+{
+    ASSERT(mBoundSurface);
+    mBoundSurface = nullptr;
+    mTexture->releaseTexImage();
+
+    // Erase the image info for level 0
+    ASSERT(mTarget == GL_TEXTURE_2D);
+    clearImageDesc(mTarget, 0);
+}
+
+void Texture::releaseTexImageInternal()
 {
     if (mBoundSurface)
     {
-        mBoundSurface = NULL;
-        mTexture->releaseTexImage();
+        // Notify the surface
+        mBoundSurface->releaseTexImageFromTexture();
 
-        // Erase the image info for level 0
-        ASSERT(mTarget == GL_TEXTURE_2D);
-        clearImageDesc(mTarget, 0);
+        // Then, call the same method as from the surface
+        releaseTexImageFromSurface();
     }
 }
 
diff --git a/src/libANGLE/Texture.h b/src/libANGLE/Texture.h
index b5a0717..bcdfb91 100644
--- a/src/libANGLE/Texture.h
+++ b/src/libANGLE/Texture.h
@@ -82,9 +82,6 @@
     bool isImmutable() const;
     GLsizei immutableLevelCount();
 
-    void bindTexImage(egl::Surface *surface);
-    void releaseTexImage();
-
     rx::TextureImpl *getImplementation() { return mTexture; }
     const rx::TextureImpl *getImplementation() const { return mTexture; }
 
@@ -93,6 +90,11 @@
   private:
     static unsigned int issueTextureSerial();
 
+    // ANGLE-only method, used internally
+    friend class egl::Surface;
+    void bindTexImageFromSurface(egl::Surface *surface);
+    void releaseTexImageFromSurface();
+
     rx::TextureImpl *mTexture;
 
     SamplerState mSamplerState;
@@ -127,6 +129,7 @@
     void setImageDescChain(size_t levels, Extents baseSize, GLenum sizedInternalFormat);
     void clearImageDesc(GLenum target, size_t level);
     void clearImageDescs();
+    void releaseTexImageInternal();
 
     std::vector<ImageDesc> mImageDescs;