Vulkan: Force flush staged updates for external textures

For textures that are backed by external memory,
immediately flush sub image updates instead of
staging them.

Bug: angleproject:4828
Tests: angle_end2end_tests --gtest_filter=ImageTest.UpdatedExternalTexture*
Change-Id: I51e5bd0cb5df7df3af21f0cdb3007eebc1be29cd
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2290490
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Mohan Maiya <m.maiya@samsung.com>
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
index 82b1790..7d100e4 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
@@ -394,6 +394,11 @@
             gl::Offset(area.x, area.y, area.z), formatInfo, unpack, type, pixels, vkFormat));
     }
 
+    if (!mOwnsImage)
+    {
+        ANGLE_TRY(mImage->flushAllStagedUpdates(contextVk));
+    }
+
     return angle::Result::Continue;
 }
 
diff --git a/src/tests/gl_tests/ImageTest.cpp b/src/tests/gl_tests/ImageTest.cpp
index b846d84..a8bfa68 100644
--- a/src/tests/gl_tests/ImageTest.cpp
+++ b/src/tests/gl_tests/ImageTest.cpp
@@ -567,6 +567,58 @@
         glDeleteFramebuffers(1, &framebuffer);
     }
 
+    void verifyResultAHB(AHardwareBuffer *source,
+                         GLubyte *referenceData,
+                         size_t dataSize,
+                         size_t bytesPerPixel)
+    {
+#if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT)
+        void *mappedMemory = nullptr;
+        GLubyte externalMemoryData[dataSize];
+        AHardwareBuffer_Desc aHardwareBufferDescription = {};
+
+        ASSERT_EQ(0, AHardwareBuffer_lock(source, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1,
+                                          nullptr, &mappedMemory));
+
+        AHardwareBuffer_describe(source, &aHardwareBufferDescription);
+        const uint32_t width    = aHardwareBufferDescription.width;
+        const uint32_t height   = aHardwareBufferDescription.height;
+        const uint32_t stride   = aHardwareBufferDescription.stride;
+        const uint32_t rowSize  = bytesPerPixel * width;
+        const size_t bufferSize = rowSize * height;
+
+        EXPECT_EQ(dataSize, bufferSize);
+
+        for (uint32_t i = 0; i < height; i++)
+        {
+            uint32_t srcPtrOffset = stride * i * bytesPerPixel;
+            uint32_t dstPtrOffset = width * i * bytesPerPixel;
+            size_t copySize       = rowSize;
+
+            if (dstPtrOffset > dataSize)
+            {
+                // Current destination ptr offset is out of range
+                break;
+            }
+            else if (dstPtrOffset + copySize > dataSize)
+            {
+                // Copy data only until the end of the buffer
+                copySize = dataSize - dstPtrOffset;
+            }
+
+            void *src = reinterpret_cast<uint8_t *>(mappedMemory) + srcPtrOffset;
+            memcpy(externalMemoryData + dstPtrOffset, src, copySize);
+        }
+
+        ASSERT_EQ(0, AHardwareBuffer_unlock(source, nullptr));
+
+        for (uint32_t i = 0; i < dataSize; i++)
+        {
+            EXPECT_EQ(externalMemoryData[i], referenceData[i]);
+        }
+#endif
+    }
+
     template <typename destType, typename sourcetype>
     destType reinterpretHelper(sourcetype source)
     {
@@ -2694,6 +2746,73 @@
     glDeleteRenderbuffers(1, &targetRenderbuffer);
 }
 
+// Check that the external texture is successfully updated when only glTexSubImage2D is called.
+TEST_P(ImageTest, UpdatedExternalTexture)
+{
+    EGLWindow *window = getEGLWindow();
+
+    ANGLE_SKIP_TEST_IF(!IsAndroid());
+    ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt());
+    ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport());
+
+    GLubyte originalData[4]      = {255, 0, 255, 255};
+    GLubyte updateData[4]        = {0, 255, 0, 255};
+    const uint32_t bytesPerPixel = 4;
+
+    // Create the Image
+    AHardwareBuffer *source;
+    EGLImageKHR image;
+    createEGLImageAndroidHardwareBufferSource(1, 1, 1, GL_RGBA8, kDefaultAttribs, originalData,
+                                              bytesPerPixel, &source, &image);
+
+    // Create target
+    GLuint targetTexture;
+    createEGLImageTargetTexture2D(image, &targetTexture);
+
+    // Expect that both the target have the original data
+    verifyResults2D(targetTexture, originalData);
+
+    // Update the data of the source
+    glBindTexture(GL_TEXTURE_2D, targetTexture);
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, updateData);
+
+    // Set sync object and flush the GL commands
+    EGLSyncKHR fence = eglCreateSyncKHR(window->getDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+    ASSERT_NE(fence, EGL_NO_SYNC_KHR);
+    glFlush();
+
+    // Delete the target texture
+    glDeleteTextures(1, &targetTexture);
+
+    // Wait that the flush command is finished
+    EGLint result = eglClientWaitSyncKHR(window->getDisplay(), fence, 0, 1000000000);
+    ASSERT_EQ(result, EGL_CONDITION_SATISFIED_KHR);
+    ASSERT_EGL_TRUE(eglDestroySyncKHR(window->getDisplay(), fence));
+
+    // Delete the EGL image
+    eglDestroyImageKHR(window->getDisplay(), image);
+
+    // Access the android hardware buffer directly to check the data is updated
+    verifyResultAHB(source, updateData, 4, bytesPerPixel);
+
+    // Create the EGL image again
+    image =
+        eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+                          angle::android::AHardwareBufferToClientBuffer(source), kDefaultAttribs);
+    ASSERT_EGL_SUCCESS();
+
+    // Create the target texture again
+    createEGLImageTargetTexture2D(image, &targetTexture);
+
+    // Expect that the target have the update data
+    verifyResults2D(targetTexture, updateData);
+
+    // Clean up
+    eglDestroyImageKHR(window->getDisplay(), image);
+    destroyAndroidHardwareBuffer(source);
+    glDeleteTextures(1, &targetTexture);
+}
+
 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
 // tests should be run against.
 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ImageTest);