Vulkan: Implement EGL Images for 2D and Renderbuffer sources.

No support for non-zero mipmaps as sources yet.

Suppress dEQP tests due to apparent driver bugs with scissored clears on depth
or stencil attachments.

BUG=angleproject:2668

Change-Id: Idaa5e70ce9b0c91232fbb989cbf4de1b9134aafb
Reviewed-on: https://chromium-review.googlesource.com/c/1415010
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Image.cpp b/src/libANGLE/Image.cpp
index 9305041..60c2d7e 100644
--- a/src/libANGLE/Image.cpp
+++ b/src/libANGLE/Image.cpp
@@ -197,6 +197,7 @@
 
 ImageState::ImageState(EGLenum target, ImageSibling *buffer, const AttributeMap &attribs)
     : label(nullptr),
+      target(target),
       imageIndex(GetImageIndex(target, attribs)),
       source(buffer),
       targets(),
@@ -242,6 +243,8 @@
 
         mState.source = nullptr;
     }
+
+    mImplementation->onDestroy(display);
 }
 
 Image::~Image()
diff --git a/src/libANGLE/Image.h b/src/libANGLE/Image.h
index e5c4562..cdcf010 100644
--- a/src/libANGLE/Image.h
+++ b/src/libANGLE/Image.h
@@ -110,6 +110,7 @@
     ~ImageState();
 
     EGLLabelKHR label;
+    EGLenum target;
     gl::ImageIndex imageIndex;
     ImageSibling *source;
     std::set<ImageSibling *> targets;
diff --git a/src/libANGLE/renderer/ImageImpl.h b/src/libANGLE/renderer/ImageImpl.h
index 5ee7aed..3ba16f5 100644
--- a/src/libANGLE/renderer/ImageImpl.h
+++ b/src/libANGLE/renderer/ImageImpl.h
@@ -45,6 +45,8 @@
   public:
     ImageImpl(const egl::ImageState &state) : mState(state) {}
     virtual ~ImageImpl() {}
+    virtual void onDestroy(const egl::Display *display) {}
+
     virtual egl::Error initialize(const egl::Display *display) = 0;
 
     virtual angle::Result orphan(const gl::Context *context, egl::ImageSibling *sibling) = 0;
diff --git a/src/libANGLE/renderer/vulkan/DisplayVk.cpp b/src/libANGLE/renderer/vulkan/DisplayVk.cpp
index 3116837..dea564a 100644
--- a/src/libANGLE/renderer/vulkan/DisplayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/DisplayVk.cpp
@@ -13,6 +13,7 @@
 #include "libANGLE/Context.h"
 #include "libANGLE/Display.h"
 #include "libANGLE/renderer/vulkan/ContextVk.h"
+#include "libANGLE/renderer/vulkan/ImageVk.h"
 #include "libANGLE/renderer/vulkan/RendererVk.h"
 #include "libANGLE/renderer/vulkan/SurfaceVk.h"
 #include "libANGLE/renderer/vulkan/SyncVk.h"
@@ -135,8 +136,7 @@
                                   EGLenum target,
                                   const egl::AttributeMap &attribs)
 {
-    UNIMPLEMENTED();
-    return static_cast<ImageImpl *>(0);
+    return new ImageVk(state, context);
 }
 
 rx::ContextImpl *DisplayVk::createContext(const gl::State &state,
@@ -183,6 +183,16 @@
 
     outExtensions->fenceSync = true;
     outExtensions->waitSync  = true;
+
+    outExtensions->image            = true;
+    outExtensions->imageBase        = true;
+    outExtensions->imagePixmap      = false;  // ANGLE does not support pixmaps
+    outExtensions->glTexture2DImage = true;
+    // TODO(geofflang): Support EGL_KHR_gl_texture_cubemap_image. http://anglebug.com/2668
+    outExtensions->glTextureCubemapImage = false;
+    // TODO(geofflang): Support ES3 and EGL_KHR_gl_texture_3D_image. http://anglebug.com/2668
+    outExtensions->glTexture3DImage    = false;
+    outExtensions->glRenderbufferImage = true;
 }
 
 void DisplayVk::generateCaps(egl::Caps *outCaps) const
diff --git a/src/libANGLE/renderer/vulkan/ImageVk.cpp b/src/libANGLE/renderer/vulkan/ImageVk.cpp
index 5ad2903..a804547 100644
--- a/src/libANGLE/renderer/vulkan/ImageVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ImageVk.cpp
@@ -11,25 +11,106 @@
 
 #include "common/debug.h"
 #include "libANGLE/Context.h"
+#include "libANGLE/Display.h"
 #include "libANGLE/renderer/vulkan/ContextVk.h"
+#include "libANGLE/renderer/vulkan/DisplayVk.h"
+#include "libANGLE/renderer/vulkan/RenderbufferVk.h"
+#include "libANGLE/renderer/vulkan/TextureVk.h"
 #include "libANGLE/renderer/vulkan/vk_utils.h"
 
 namespace rx
 {
 
-ImageVk::ImageVk(const egl::ImageState &state) : ImageImpl(state) {}
+ImageVk::ImageVk(const egl::ImageState &state, const gl::Context *context)
+    : ImageImpl(state), mOwnsImage(false), mImage(nullptr), mContext(context)
+{}
 
 ImageVk::~ImageVk() {}
 
+void ImageVk::onDestroy(const egl::Display *display)
+{
+    DisplayVk *displayVk = vk::GetImpl(display);
+    RendererVk *renderer = displayVk->getRenderer();
+
+    if (mImage != nullptr && mOwnsImage)
+    {
+        mImage->releaseImage(renderer);
+        mImage->releaseStagingBuffer(renderer);
+        delete mImage;
+    }
+    mImage = nullptr;
+}
+
 egl::Error ImageVk::initialize(const egl::Display *display)
 {
-    UNIMPLEMENTED();
-    return egl::EglBadAccess();
+
+    if (egl::IsTextureTarget(mState.target))
+    {
+        TextureVk *textureVk = GetImplAs<TextureVk>(GetAs<gl::Texture>(mState.source));
+
+        // Make sure the texture has created its backing storage
+        ASSERT(mContext != nullptr);
+        ContextVk *contextVk = vk::GetImpl(mContext);
+        ANGLE_TRY(ResultToEGL(textureVk->ensureImageInitialized(contextVk)));
+
+        mImage = &textureVk->getImage();
+
+        // The staging buffer for a texture source should already be initialized
+
+        mOwnsImage = false;
+
+        ASSERT(mState.imageIndex.getLevelIndex() == 0);
+    }
+    else if (egl::IsRenderbufferTarget(mState.target))
+    {
+        RenderbufferVk *renderbufferVk =
+            GetImplAs<RenderbufferVk>(GetAs<gl::Renderbuffer>(mState.source));
+        mImage = renderbufferVk->getImage();
+
+        // Make sure a staging buffer is ready to use to upload data
+        ASSERT(mContext != nullptr);
+        ContextVk *contextVk = vk::GetImpl(mContext);
+        RendererVk *renderer = contextVk->getRenderer();
+        mImage->initStagingBuffer(renderer);
+
+        mOwnsImage = false;
+    }
+    else
+    {
+        UNREACHABLE();
+        return egl::EglBadAccess();
+    }
+
+    return egl::NoError();
 }
 
 angle::Result ImageVk::orphan(const gl::Context *context, egl::ImageSibling *sibling)
 {
-    ANGLE_VK_UNREACHABLE(vk::GetImpl(context));
-    return angle::Result::Stop;
+    if (sibling == mState.source)
+    {
+        if (egl::IsTextureTarget(mState.target))
+        {
+            TextureVk *textureVk = GetImplAs<TextureVk>(GetAs<gl::Texture>(mState.source));
+            ASSERT(mImage == &textureVk->getImage());
+            textureVk->releaseOwnershipOfImage(context);
+            mOwnsImage = true;
+        }
+        else if (egl::IsRenderbufferTarget(mState.target))
+        {
+            RenderbufferVk *renderbufferVk =
+                GetImplAs<RenderbufferVk>(GetAs<gl::Renderbuffer>(mState.source));
+            ASSERT(mImage == renderbufferVk->getImage());
+            renderbufferVk->releaseOwnershipOfImage(context);
+            mOwnsImage = true;
+        }
+        else
+        {
+            ANGLE_VK_UNREACHABLE(vk::GetImpl(context));
+            return angle::Result::Stop;
+        }
+    }
+
+    return angle::Result::Continue;
 }
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/ImageVk.h b/src/libANGLE/renderer/vulkan/ImageVk.h
index c6611b7..f93eb27 100644
--- a/src/libANGLE/renderer/vulkan/ImageVk.h
+++ b/src/libANGLE/renderer/vulkan/ImageVk.h
@@ -11,6 +11,7 @@
 #define LIBANGLE_RENDERER_VULKAN_IMAGEVK_H_
 
 #include "libANGLE/renderer/ImageImpl.h"
+#include "libANGLE/renderer/vulkan/vk_helpers.h"
 
 namespace rx
 {
@@ -18,11 +19,21 @@
 class ImageVk : public ImageImpl
 {
   public:
-    ImageVk(const egl::ImageState &state);
+    ImageVk(const egl::ImageState &state, const gl::Context *context);
     ~ImageVk() override;
+    void onDestroy(const egl::Display *display) override;
+
     egl::Error initialize(const egl::Display *display) override;
 
     angle::Result orphan(const gl::Context *context, egl::ImageSibling *sibling) override;
+
+    vk::ImageHelper *getImage() const { return mImage; }
+
+  private:
+    bool mOwnsImage;
+    vk::ImageHelper *mImage;
+
+    const gl::Context *mContext;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp b/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
index 9eb9445..5640703 100644
--- a/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
@@ -10,7 +10,9 @@
 #include "libANGLE/renderer/vulkan/RenderbufferVk.h"
 
 #include "libANGLE/Context.h"
+#include "libANGLE/Image.h"
 #include "libANGLE/renderer/vulkan/ContextVk.h"
+#include "libANGLE/renderer/vulkan/ImageVk.h"
 #include "libANGLE/renderer/vulkan/RendererVk.h"
 
 namespace rx
@@ -24,7 +26,7 @@
 }  // anonymous namespace
 
 RenderbufferVk::RenderbufferVk(const gl::RenderbufferState &state)
-    : RenderbufferImpl(state), mImage(nullptr)
+    : RenderbufferImpl(state), mOwnsImage(false), mImage(nullptr)
 {}
 
 RenderbufferVk::~RenderbufferVk() {}
@@ -33,15 +35,7 @@
 {
     ContextVk *contextVk = vk::GetImpl(context);
     RendererVk *renderer = contextVk->getRenderer();
-
-    if (mImage)
-    {
-        mImage->releaseImage(renderer);
-        mImage->releaseStagingBuffer(renderer);
-        SafeDelete(mImage);
-    }
-
-    renderer->releaseObject(renderer->getCurrentQueueSerial(), &mImageView);
+    releaseAndDeleteImage(context, renderer);
 }
 
 angle::Result RenderbufferVk::setStorage(const gl::Context *context,
@@ -53,6 +47,11 @@
     RendererVk *renderer       = contextVk->getRenderer();
     const vk::Format &vkFormat = renderer->getFormat(internalformat);
 
+    if (!mOwnsImage)
+    {
+        releaseAndDeleteImage(context, renderer);
+    }
+
     if (mImage != nullptr && mImage->valid())
     {
         // Check against the state if we need to recreate the storage.
@@ -60,8 +59,7 @@
             static_cast<GLsizei>(width) != mState.getWidth() ||
             static_cast<GLsizei>(height) != mState.getHeight())
         {
-            mImage->releaseImage(renderer);
-            renderer->releaseObject(renderer->getCurrentQueueSerial(), &mImageView);
+            releaseImage(context, renderer);
         }
     }
 
@@ -69,7 +67,8 @@
     {
         if (mImage == nullptr)
         {
-            mImage = new vk::ImageHelper();
+            mImage     = new vk::ImageHelper();
+            mOwnsImage = true;
         }
 
         const angle::Format &textureFormat = vkFormat.textureFormat();
@@ -126,8 +125,26 @@
 angle::Result RenderbufferVk::setStorageEGLImageTarget(const gl::Context *context,
                                                        egl::Image *image)
 {
-    ANGLE_VK_UNREACHABLE(vk::GetImpl(context));
-    return angle::Result::Stop;
+    ContextVk *contextVk = vk::GetImpl(context);
+    RendererVk *renderer = contextVk->getRenderer();
+
+    releaseAndDeleteImage(context, renderer);
+
+    ImageVk *imageVk = vk::GetImpl(image);
+    mImage           = imageVk->getImage();
+    mOwnsImage       = false;
+
+    const vk::Format &vkFormat = renderer->getFormat(image->getFormat().info->sizedInternalFormat);
+    const angle::Format &textureFormat = vkFormat.textureFormat();
+
+    VkImageAspectFlags aspect = vk::GetFormatAspectFlags(textureFormat);
+
+    ANGLE_TRY(mImage->initImageView(contextVk, gl::TextureType::_2D, aspect, gl::SwizzleState(),
+                                    &mImageView, 1));
+
+    mRenderTarget.init(mImage, &mImageView, 0, nullptr);
+
+    return angle::Result::Continue;
 }
 
 angle::Result RenderbufferVk::getAttachmentRenderTarget(const gl::Context *context,
@@ -147,4 +164,34 @@
     return angle::Result::Continue;
 }
 
+void RenderbufferVk::releaseOwnershipOfImage(const gl::Context *context)
+{
+    ContextVk *contextVk = vk::GetImpl(context);
+    RendererVk *renderer = contextVk->getRenderer();
+
+    mOwnsImage = false;
+    releaseAndDeleteImage(context, renderer);
+}
+
+void RenderbufferVk::releaseAndDeleteImage(const gl::Context *context, RendererVk *renderer)
+{
+    releaseImage(context, renderer);
+    SafeDelete(mImage);
+}
+
+void RenderbufferVk::releaseImage(const gl::Context *context, RendererVk *renderer)
+{
+    if (mImage && mOwnsImage)
+    {
+        mImage->releaseImage(renderer);
+        mImage->releaseStagingBuffer(renderer);
+    }
+    else
+    {
+        mImage = nullptr;
+    }
+
+    renderer->releaseObject(renderer->getCurrentQueueSerial(), &mImageView);
+}
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/RenderbufferVk.h b/src/libANGLE/renderer/vulkan/RenderbufferVk.h
index 67d94b6..57c6ccc 100644
--- a/src/libANGLE/renderer/vulkan/RenderbufferVk.h
+++ b/src/libANGLE/renderer/vulkan/RenderbufferVk.h
@@ -44,7 +44,14 @@
     angle::Result initializeContents(const gl::Context *context,
                                      const gl::ImageIndex &imageIndex) override;
 
+    vk::ImageHelper *getImage() const { return mImage; }
+    void releaseOwnershipOfImage(const gl::Context *context);
+
   private:
+    void releaseAndDeleteImage(const gl::Context *context, RendererVk *renderer);
+    void releaseImage(const gl::Context *context, RendererVk *renderer);
+
+    bool mOwnsImage;
     vk::ImageHelper *mImage;
     vk::ImageView mImageView;
     RenderTargetVk mRenderTarget;
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
index 0607606..ab346c1 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
@@ -13,9 +13,11 @@
 #include "image_util/generatemip.inl"
 #include "libANGLE/Config.h"
 #include "libANGLE/Context.h"
+#include "libANGLE/Image.h"
 #include "libANGLE/Surface.h"
 #include "libANGLE/renderer/vulkan/ContextVk.h"
 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
+#include "libANGLE/renderer/vulkan/ImageVk.h"
 #include "libANGLE/renderer/vulkan/RendererVk.h"
 #include "libANGLE/renderer/vulkan/SurfaceVk.h"
 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
@@ -528,6 +530,11 @@
     ContextVk *contextVk             = GetAs<ContextVk>(context->getImplementation());
     RendererVk *renderer             = contextVk->getRenderer();
 
+    if (!mOwnsImage)
+    {
+        releaseAndDeleteImage(context, renderer);
+    }
+
     ANGLE_TRY(ensureImageAllocated(renderer));
 
     const vk::Format &format         = renderer->getFormat(internalFormat);
@@ -547,8 +554,18 @@
                                            gl::TextureType type,
                                            egl::Image *image)
 {
-    ANGLE_VK_UNREACHABLE(vk::GetImpl(context));
-    return angle::Result::Stop;
+    ContextVk *contextVk = vk::GetImpl(context);
+    RendererVk *renderer = contextVk->getRenderer();
+
+    releaseAndDeleteImage(context, renderer);
+
+    ImageVk *imageVk = vk::GetImpl(image);
+    setImageHelper(renderer, imageVk->getImage(), false);
+
+    const vk::Format &format = renderer->getFormat(image->getFormat().info->sizedInternalFormat);
+    ANGLE_TRY(initImageViews(contextVk, format, 1));
+
+    return angle::Result::Continue;
 }
 
 angle::Result TextureVk::setImageExternal(const gl::Context *context,
@@ -602,9 +619,10 @@
     ContextVk *contextVk = vk::GetImpl(context);
     RendererVk *renderer = contextVk->getRenderer();
 
-    // Make sure the image is not allocated yet or it is owned by this texture. We don't want to
-    // redefine an external image.
-    ASSERT(mImage == nullptr || mOwnsImage);
+    if (!mOwnsImage)
+    {
+        releaseAndDeleteImage(context, renderer);
+    }
 
     if (!size.empty())
     {
@@ -929,6 +947,15 @@
     return angle::Result::Continue;
 }
 
+void TextureVk::releaseOwnershipOfImage(const gl::Context *context)
+{
+    ContextVk *contextVk = vk::GetImpl(context);
+    RendererVk *renderer = contextVk->getRenderer();
+
+    mOwnsImage = false;
+    releaseAndDeleteImage(context, renderer);
+}
+
 const vk::ImageView &TextureVk::getReadImageView() const
 {
     ASSERT(mImage->valid());
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.h b/src/libANGLE/renderer/vulkan/TextureVk.h
index 33fceff..35c00db 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.h
+++ b/src/libANGLE/renderer/vulkan/TextureVk.h
@@ -139,6 +139,8 @@
         return *mImage;
     }
 
+    void releaseOwnershipOfImage(const gl::Context *context);
+
     const vk::ImageView &getReadImageView() const;
     angle::Result getLayerLevelDrawImageView(vk::Context *context,
                                              size_t layer,
diff --git a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
index c9a2cbd..ef3917c 100644
--- a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
@@ -50,6 +50,12 @@
     outExtensions->textureBorderClamp     = false;  // not implemented yet
     outExtensions->translatedShaderSource = true;
 
+    outExtensions->eglImage = true;
+    // TODO(geofflang): Support GL_OES_EGL_image_external. http://anglebug.com/2668
+    outExtensions->eglImageExternal = false;
+    // TODO(geofflang): Support GL_OES_EGL_image_external_essl3. http://anglebug.com/2668
+    outExtensions->eglImageExternalEssl3 = false;
+
     // Only expose robust buffer access if the physical device supports it.
     outExtensions->robustBufferAccessBehavior = physicalDeviceFeatures.robustBufferAccess;
 
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index ed1eb30..75ed286 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -1958,7 +1958,7 @@
                                    newBufferAllocatedOut);
 }
 
-angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
+angle::Result ImageHelper::flushStagedUpdates(Context *context,
                                               uint32_t levelCount,
                                               vk::CommandBuffer *commandBuffer)
 {
@@ -1967,9 +1967,9 @@
         return angle::Result::Continue;
     }
 
-    RendererVk *renderer = contextVk->getRenderer();
+    RendererVk *renderer = context->getRenderer();
 
-    ANGLE_TRY(mStagingBuffer.flush(contextVk));
+    ANGLE_TRY(mStagingBuffer.flush(context));
 
     std::vector<SubresourceUpdate> updatesToKeep;
 
@@ -2025,7 +2025,7 @@
 
     if (mSubresourceUpdates.empty())
     {
-        mStagingBuffer.releaseRetainedBuffers(contextVk->getRenderer());
+        mStagingBuffer.releaseRetainedBuffers(context->getRenderer());
     }
     else
     {
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h
index bb4592c..bbf79bc 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.h
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.h
@@ -659,7 +659,7 @@
                                         VkDeviceSize *offsetOut,
                                         bool *newBufferAllocatedOut);
 
-    angle::Result flushStagedUpdates(ContextVk *contextVk,
+    angle::Result flushStagedUpdates(Context *context,
                                      uint32_t levelCount,
                                      vk::CommandBuffer *commandBuffer);
 
diff --git a/src/libANGLE/renderer/vulkan/vk_utils.h b/src/libANGLE/renderer/vulkan/vk_utils.h
index 8e762e8..70c12b7 100644
--- a/src/libANGLE/renderer/vulkan/vk_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_utils.h
@@ -33,6 +33,7 @@
 namespace egl
 {
 class Display;
+class Image;
 }
 
 namespace gl
@@ -55,6 +56,7 @@
 {
 class CommandGraphResource;
 class DisplayVk;
+class ImageVk;
 class RenderTargetVk;
 class RendererVk;
 class RenderPassCache;
@@ -136,6 +138,12 @@
     using ImplType = DisplayVk;
 };
 
+template <>
+struct ImplTypeHelper<egl::Image>
+{
+    using ImplType = ImageVk;
+};
+
 template <typename T>
 using GetImplType = typename ImplTypeHelper<T>::ImplType;