Support creating EGLImage from VkImage
Bug: chromium:1264439
Change-Id: I520182143e748f25b44d0725f3f171b7b33a85d8
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3311131
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Peng Huang <penghuang@chromium.org>
diff --git a/extensions/EGL_ANGLE_vulkan_image.txt b/extensions/EGL_ANGLE_vulkan_image.txt
index 045767a..37b0ff5 100644
--- a/extensions/EGL_ANGLE_vulkan_image.txt
+++ b/extensions/EGL_ANGLE_vulkan_image.txt
@@ -20,7 +20,7 @@
Version
- Version 1, Nov 17, 2021
+ Version 2, Dec 10, 2021
Number
@@ -51,7 +51,41 @@
New Tokens
- None
+ EGL_VULKAN_IMAGE_ANGLE 0x34D3
+ EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE 0x34D4
+ EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE 0x34D5
+
+Additions to Chapter 2 of the EGL 1.4 Specification (EGL Operation)
+
+ Add to section 2.5.1 "EGLImage Specification" (as defined by the
+ EGL_KHR_image_base specification), in the description of
+ eglCreateImageKHR:
+
+ "Values accepted for <target> are listed in Table aaa, below.
+
+ +----------------------------+-----------------------------------------+
+ | <target> | Notes |
+ +----------------------------+-----------------------------------------+
+ | EGL_VULKAN_IMAGE_ANGLE | Used for VkImage objects |
+ +----------------------------+-----------------------------------------+
+ Table aaa. Legal values for eglCreateImageKHR <target> parameter
+
+ ...
+
+ If <target> is EGL_VULKAN_IMAGE_ANGLE, <dpy> must be a valid display, <ctx>
+ must be EGL_NO_CONTEXT, <buffer> must be a pointer to a valid VkImage
+ (cast into the type EGLClientBuffer), the VkImage must be created with the
+ same VkDevice used by GL implementation and attributes other than
+ EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE or
+ EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE are ignored.
+
+ EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE and
+ EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE must be specified. They contain
+ hi 32bits and lo 32bits of a pointer to the memory stores a valid
+ VkImageCreateInfo structure. The GL implementation will get all necessary
+ info of the VkImage from it. All supported structures in the pNext structure
+ chain will be parsed, not supported structures will be ignored.
+
Additions to the EGL 1.4 Specification:
@@ -74,4 +108,6 @@
Revision History
- Version 1, 2021/11/17 - first draft.
\ No newline at end of file
+ Version 1, 2021/11/17 - first draft.
+
+ Version 2, 2021/12/10 - add support for creating EGLImageKHR from VkImage.
\ No newline at end of file
diff --git a/include/EGL/eglext_angle.h b/include/EGL/eglext_angle.h
index 74f3b9d..ff15684 100644
--- a/include/EGL/eglext_angle.h
+++ b/include/EGL/eglext_angle.h
@@ -376,6 +376,9 @@
#ifndef EGL_ANGLE_vulkan_image
#define EGL_ANGLE_vulkan_image
+#define EGL_VULKAN_IMAGE_ANGLE 0x34D3
+#define EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE 0x34D4
+#define EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE 0x34D5
typedef EGLBoolean (EGLAPIENTRYP PFNEGLEXPORTVKIMAGEANGLEPROC)(EGLDisplay dpy, EGLImage image, void* vk_image, void* vk_image_create_info);
#endif /* EGL_ANGLE_vulkan_image */
diff --git a/src/common/utilities.cpp b/src/common/utilities.cpp
index bf15015..ebe282c 100644
--- a/src/common/utilities.cpp
+++ b/src/common/utilities.cpp
@@ -1275,6 +1275,7 @@
case EGL_D3D11_TEXTURE_ANGLE:
case EGL_LINUX_DMA_BUF_EXT:
case EGL_METAL_TEXTURE_ANGLE:
+ case EGL_VULKAN_IMAGE_ANGLE:
return true;
default:
diff --git a/src/libANGLE/renderer/vulkan/BUILD.gn b/src/libANGLE/renderer/vulkan/BUILD.gn
index 980dd46..f0ceccc 100644
--- a/src/libANGLE/renderer/vulkan/BUILD.gn
+++ b/src/libANGLE/renderer/vulkan/BUILD.gn
@@ -84,6 +84,8 @@
"UtilsVk.h",
"VertexArrayVk.cpp",
"VertexArrayVk.h",
+ "VkImageImageSiblingVk.cpp",
+ "VkImageImageSiblingVk.h",
"VulkanSecondaryCommandBuffer.cpp",
"VulkanSecondaryCommandBuffer.h",
"android/vk_android_utils.cpp",
diff --git a/src/libANGLE/renderer/vulkan/DisplayVk.cpp b/src/libANGLE/renderer/vulkan/DisplayVk.cpp
index 9158d5c..4adf94a 100644
--- a/src/libANGLE/renderer/vulkan/DisplayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/DisplayVk.cpp
@@ -18,6 +18,7 @@
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/SurfaceVk.h"
#include "libANGLE/renderer/vulkan/SyncVk.h"
+#include "libANGLE/renderer/vulkan/VkImageImageSiblingVk.h"
#include "libANGLE/trace.h"
namespace rx
@@ -202,6 +203,54 @@
return mRenderer->getMaxConformantESVersion();
}
+egl::Error DisplayVk::validateImageClientBuffer(const gl::Context *context,
+ EGLenum target,
+ EGLClientBuffer clientBuffer,
+ const egl::AttributeMap &attribs) const
+{
+ switch (target)
+ {
+ case EGL_VULKAN_IMAGE_ANGLE:
+ {
+ VkImage *vkImage = reinterpret_cast<VkImage *>(clientBuffer);
+ if (!vkImage || *vkImage == VK_NULL_HANDLE)
+ {
+ return egl::EglBadParameter() << "clientBuffer is invalid.";
+ }
+
+ uint64_t hi = static_cast<uint64_t>(attribs.get(EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE));
+ uint64_t lo = static_cast<uint64_t>(attribs.get(EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE));
+ uint64_t info = ((hi & 0xffffffff) << 32) | (lo & 0xffffffff);
+ if (reinterpret_cast<const VkImageCreateInfo *>(info)->sType !=
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO)
+ {
+ return egl::EglBadParameter()
+ << "EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE and "
+ "EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE are not pointing to a "
+ "valid VkImageCreateInfo structure.";
+ }
+
+ return egl::NoError();
+ }
+ default:
+ return DisplayImpl::validateImageClientBuffer(context, target, clientBuffer, attribs);
+ }
+}
+
+ExternalImageSiblingImpl *DisplayVk::createExternalImageSibling(const gl::Context *context,
+ EGLenum target,
+ EGLClientBuffer buffer,
+ const egl::AttributeMap &attribs)
+{
+ switch (target)
+ {
+ case EGL_VULKAN_IMAGE_ANGLE:
+ return new VkImageImageSiblingVk(buffer, attribs);
+ default:
+ return DisplayImpl::createExternalImageSibling(context, target, buffer, attribs);
+ }
+}
+
void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const
{
outExtensions->createContextRobustness = getRenderer()->getNativeExtensions().robustnessEXT;
diff --git a/src/libANGLE/renderer/vulkan/DisplayVk.h b/src/libANGLE/renderer/vulkan/DisplayVk.h
index 7fca3f5..0a1305e 100644
--- a/src/libANGLE/renderer/vulkan/DisplayVk.h
+++ b/src/libANGLE/renderer/vulkan/DisplayVk.h
@@ -117,6 +117,14 @@
gl::Version getMaxSupportedESVersion() const override;
gl::Version getMaxConformantESVersion() const override;
+ egl::Error validateImageClientBuffer(const gl::Context *context,
+ EGLenum target,
+ EGLClientBuffer clientBuffer,
+ const egl::AttributeMap &attribs) const override;
+ ExternalImageSiblingImpl *createExternalImageSibling(const gl::Context *context,
+ EGLenum target,
+ EGLClientBuffer buffer,
+ const egl::AttributeMap &attribs) override;
virtual const char *getWSIExtension() const = 0;
virtual const char *getWSILayer() const;
virtual bool isUsingSwapchain() const;
diff --git a/src/libANGLE/renderer/vulkan/VkImageImageSiblingVk.cpp b/src/libANGLE/renderer/vulkan/VkImageImageSiblingVk.cpp
new file mode 100644
index 0000000..bf16c6c
--- /dev/null
+++ b/src/libANGLE/renderer/vulkan/VkImageImageSiblingVk.cpp
@@ -0,0 +1,126 @@
+//
+// Copyright 2021 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// VkImageImageSiblingVk.cpp: Implements VkImageImageSiblingVk.
+
+#include "libANGLE/renderer/vulkan/VkImageImageSiblingVk.h"
+
+#include "libANGLE/Display.h"
+#include "libANGLE/renderer/vulkan/DisplayVk.h"
+#include "libANGLE/renderer/vulkan/RendererVk.h"
+
+namespace rx
+{
+
+VkImageImageSiblingVk::VkImageImageSiblingVk(EGLClientBuffer buffer,
+ const egl::AttributeMap &attribs)
+{
+ mVkImage.setHandle(*reinterpret_cast<VkImage *>(buffer));
+
+ ASSERT(attribs.contains(EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE));
+ ASSERT(attribs.contains(EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE));
+ uint64_t hi = static_cast<uint64_t>(attribs.get(EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE));
+ uint64_t lo = static_cast<uint64_t>(attribs.get(EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE));
+ const VkImageCreateInfo *info =
+ reinterpret_cast<const VkImageCreateInfo *>((hi << 32) | (lo & 0xffffffff));
+ ASSERT(info->sType == VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO);
+ mVkImageInfo = *info;
+ // TODO(penghuang): support extensions.
+ mVkImageInfo.pNext = nullptr;
+}
+
+VkImageImageSiblingVk::~VkImageImageSiblingVk() = default;
+
+egl::Error VkImageImageSiblingVk::initialize(const egl::Display *display)
+{
+ DisplayVk *displayVk = vk::GetImpl(display);
+ return angle::ToEGL(initImpl(displayVk), displayVk, EGL_BAD_PARAMETER);
+}
+
+angle::Result VkImageImageSiblingVk::initImpl(DisplayVk *displayVk)
+{
+ RendererVk *renderer = displayVk->getRenderer();
+
+ const angle::FormatID formatID = vk::GetFormatIDFromVkFormat(mVkImageInfo.format);
+ ANGLE_VK_CHECK(displayVk, formatID != angle::FormatID::NONE, VK_ERROR_FORMAT_NOT_SUPPORTED);
+
+ const vk::Format &vkFormat = renderer->getFormat(formatID);
+ const angle::FormatID intendedFormatID = vkFormat.getIntendedFormatID();
+ const vk::ImageAccess imageAccess =
+ isRenderable(nullptr) ? vk::ImageAccess::Renderable : vk::ImageAccess::SampleOnly;
+ const angle::FormatID actualImageFormatID = vkFormat.getActualImageFormatID(imageAccess);
+ const angle::Format &format = angle::Format::Get(actualImageFormatID);
+ mFormat = gl::Format(format.glInternalFormat);
+
+ // Create the image
+ mImage = new vk::ImageHelper();
+ constexpr bool kIsRobustInitEnabled = false;
+ mImage->init2DWeakReference(displayVk, mVkImage.release(), getSize(), false, intendedFormatID,
+ actualImageFormatID, 1, kIsRobustInitEnabled);
+
+ return angle::Result::Continue;
+}
+
+void VkImageImageSiblingVk::onDestroy(const egl::Display *display)
+{
+ ASSERT(mImage == nullptr);
+}
+
+gl::Format VkImageImageSiblingVk::getFormat() const
+{
+ return mFormat;
+}
+
+bool VkImageImageSiblingVk::isRenderable(const gl::Context *context) const
+{
+ return mVkImageInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+}
+
+bool VkImageImageSiblingVk::isTexturable(const gl::Context *context) const
+{
+ return mVkImageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT;
+}
+
+bool VkImageImageSiblingVk::isYUV() const
+{
+ return false;
+}
+
+bool VkImageImageSiblingVk::hasProtectedContent() const
+{
+ return false;
+}
+
+gl::Extents VkImageImageSiblingVk::getSize() const
+{
+ return gl::Extents(mVkImageInfo.extent.width, mVkImageInfo.extent.height,
+ mVkImageInfo.extent.depth);
+}
+
+size_t VkImageImageSiblingVk::getSamples() const
+{
+ return 0;
+}
+
+// ExternalImageSiblingVk interface
+vk::ImageHelper *VkImageImageSiblingVk::getImage() const
+{
+ return mImage;
+}
+
+void VkImageImageSiblingVk::release(RendererVk *renderer)
+{
+ if (mImage != nullptr)
+ {
+ // TODO: Handle the case where the EGLImage is used in two contexts not in the same share
+ // group. https://issuetracker.google.com/169868803
+ mImage->resetImageWeakReference();
+ mImage->destroy(renderer);
+ SafeDelete(mImage);
+ }
+}
+
+} // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/VkImageImageSiblingVk.h b/src/libANGLE/renderer/vulkan/VkImageImageSiblingVk.h
new file mode 100644
index 0000000..571eac6
--- /dev/null
+++ b/src/libANGLE/renderer/vulkan/VkImageImageSiblingVk.h
@@ -0,0 +1,52 @@
+//
+// Copyright 2021 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// VkImageImageSiblingVk.h: Defines the VkImageImageSiblingVk to wrap
+// EGL images created from VkImage.
+
+#ifndef LIBANGLE_RENDERER_VULKAN_VKIMAGEIMAGESIBLINGVK_H_
+#define LIBANGLE_RENDERER_VULKAN_VKIMAGEIMAGESIBLINGVK_H_
+
+#include "libANGLE/renderer/vulkan/ImageVk.h"
+
+namespace rx
+{
+
+class VkImageImageSiblingVk final : public ExternalImageSiblingVk
+{
+ public:
+ VkImageImageSiblingVk(EGLClientBuffer buffer, const egl::AttributeMap &attribs);
+ ~VkImageImageSiblingVk() override;
+
+ egl::Error initialize(const egl::Display *display) override;
+ void onDestroy(const egl::Display *display) override;
+
+ // ExternalImageSiblingImpl interface
+ gl::Format getFormat() const override;
+ bool isRenderable(const gl::Context *context) const override;
+ bool isTexturable(const gl::Context *context) const override;
+ bool isYUV() const override;
+ bool hasProtectedContent() const override;
+ gl::Extents getSize() const override;
+ size_t getSamples() const override;
+
+ // ExternalImageSiblingVk interface
+ vk::ImageHelper *getImage() const override;
+
+ void release(RendererVk *renderer) override;
+
+ private:
+ angle::Result initImpl(DisplayVk *displayVk);
+
+ vk::Image mVkImage;
+ VkImageCreateInfo mVkImageInfo;
+ gl::Format mFormat{GL_NONE};
+ vk::ImageHelper *mImage = nullptr;
+};
+
+} // namespace rx
+
+#endif // LIBANGLE_RENDERER_VULKAN_VKIMAGEIMAGESIBLINGVK_H_
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index b0ec858..f7c1791 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -4793,9 +4793,10 @@
mActualFormatID = actualFormatID;
mSamples = std::max(samples, 1);
mImageSerial = context->getRenderer()->getResourceSerialFactory().generateImageSerial();
- mCurrentLayout = ImageLayout::Undefined;
- mLayerCount = 1;
- mLevelCount = 1;
+ mCurrentQueueFamilyIndex = context->getRenderer()->getQueueFamilyIndex();
+ mCurrentLayout = ImageLayout::Undefined;
+ mLayerCount = 1;
+ mLevelCount = 1;
mImage.setHandle(handle);
diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp
index ce4f937..c854cb0 100644
--- a/src/libANGLE/validationEGL.cpp
+++ b/src/libANGLE/validationEGL.cpp
@@ -3366,6 +3366,17 @@
}
break;
+ case EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE:
+ case EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE:
+ if (!displayExtensions.vulkanImageANGLE)
+ {
+ val->setError(EGL_BAD_ATTRIBUTE,
+ "Attribute EGL_VULKAN_IMAGE_CREATE_INFO_{HI,LO}_ANGLE require "
+ "extension EGL_ANGLE_vulkan_image.");
+ return false;
+ }
+ break;
+
default:
val->setError(EGL_BAD_PARAMETER, "invalid attribute: 0x%04" PRIxPTR "X", attribute);
return false;
@@ -3739,6 +3750,42 @@
display->validateImageClientBuffer(context, target, buffer, attributes),
val->entryPoint, val->labeledObject, false);
break;
+ case EGL_VULKAN_IMAGE_ANGLE:
+ if (!displayExtensions.vulkanImageANGLE)
+ {
+ val->setError(EGL_BAD_PARAMETER, "EGL_ANGLE_vulkan_image not supported.");
+ return false;
+ }
+
+ if (context != nullptr)
+ {
+ val->setError(EGL_BAD_CONTEXT, "ctx must be EGL_NO_CONTEXT.");
+ return false;
+ }
+
+ {
+ const EGLenum kRequiredParameters[] = {
+ EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE,
+ EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE,
+ };
+ for (EGLenum requiredParameter : kRequiredParameters)
+ {
+ if (!attributes.contains(requiredParameter))
+ {
+ val->setError(EGL_BAD_PARAMETER,
+ "Missing required parameter 0x%X for image target "
+ "EGL_VULKAN_IMAGE_ANGLE.",
+ requiredParameter);
+ return false;
+ }
+ }
+ }
+
+ ANGLE_EGL_TRY_RETURN(
+ val->eglThread,
+ display->validateImageClientBuffer(context, target, buffer, attributes),
+ val->entryPoint, val->labeledObject, false);
+ break;
default:
val->setError(EGL_BAD_PARAMETER, "invalid target: 0x%X", target);
return false;
diff --git a/src/tests/capture_replay_tests/capture_replay_expectations.txt b/src/tests/capture_replay_tests/capture_replay_expectations.txt
index 0e2aec7..dce32d8 100644
--- a/src/tests/capture_replay_tests/capture_replay_expectations.txt
+++ b/src/tests/capture_replay_tests/capture_replay_expectations.txt
@@ -216,4 +216,4 @@
6663 : Texture2DTest.UploadThenFSThenVS/* = NOT_RUN
6663 : Texture2DTest.UploadThenFSThenVSThenNewRPThenFS/* = NOT_RUN
-6741 : VulkanImageTest.PixelTest* = SKIP_FOR_CAPTURE
+6741 : VulkanImageTest.* = SKIP_FOR_CAPTURE
diff --git a/src/tests/gl_tests/VulkanImageTest.cpp b/src/tests/gl_tests/VulkanImageTest.cpp
index f378582..a58f850 100644
--- a/src/tests/gl_tests/VulkanImageTest.cpp
+++ b/src/tests/gl_tests/VulkanImageTest.cpp
@@ -22,11 +22,30 @@
using VulkanImageTest = ANGLETest;
+// Check extensions with Vukan backend.
+TEST_P(VulkanImageTest, HasVulkanImageExtensions)
+{
+ ANGLE_SKIP_TEST_IF(!IsVulkan());
+
+ EGLWindow *window = getEGLWindow();
+ EGLDisplay display = window->getDisplay();
+
+ EXPECT_TRUE(IsEGLClientExtensionEnabled("EGL_EXT_device_query"));
+ EXPECT_TRUE(IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_vulkan_image"));
+ EXPECT_TRUE(IsGLExtensionEnabled("GL_ANGLE_vulkan_image"));
+
+ EGLAttrib result = 0;
+ EXPECT_EGL_TRUE(eglQueryDisplayAttribEXT(display, EGL_DEVICE_EXT, &result));
+
+ EGLDeviceEXT device = reinterpret_cast<EGLDeviceEXT>(result);
+ EXPECT_NE(EGL_NO_DEVICE_EXT, device);
+ EXPECT_TRUE(IsEGLDeviceExtensionEnabled(device, "EGL_ANGLE_device_vulkan"));
+}
+
TEST_P(VulkanImageTest, DeviceVulkan)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
- EXPECT_TRUE(IsEGLClientExtensionEnabled("EGL_EXT_device_query"));
EGLWindow *window = getEGLWindow();
EGLDisplay display = window->getDisplay();
@@ -35,7 +54,6 @@
EGLDeviceEXT device = reinterpret_cast<EGLDeviceEXT>(result);
EXPECT_NE(EGL_NO_DEVICE_EXT, device);
- EXPECT_TRUE(IsEGLDeviceExtensionEnabled(device, "EGL_ANGLE_device_vulkan"));
EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(device, EGL_VULKAN_INSTANCE_ANGLE, &result));
VkInstance instance = reinterpret_cast<VkInstance>(result);
@@ -95,13 +113,9 @@
TEST_P(VulkanImageTest, ExportVKImage)
{
- ANGLE_SKIP_TEST_IF(!IsVulkan());
-
EGLWindow *window = getEGLWindow();
EGLDisplay display = window->getDisplay();
-
- EXPECT_TRUE(IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_vulkan_image"));
- EXPECT_TRUE(IsGLExtensionEnabled("GL_ANGLE_vulkan_image"));
+ ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_vulkan_image"));
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
@@ -133,18 +147,16 @@
EXPECT_EGL_TRUE(eglDestroyImageKHR(display, eglImage));
}
+// Check pixels after glTexImage2D
TEST_P(VulkanImageTest, PixelTestTexImage2D)
{
- ANGLE_SKIP_TEST_IF(!IsVulkan());
-
- VulkanHelper helper;
- helper.initializeFromANGLE();
-
EGLWindow *window = getEGLWindow();
EGLDisplay display = window->getDisplay();
- EXPECT_TRUE(IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_vulkan_image"));
- EXPECT_TRUE(IsGLExtensionEnabled("GL_ANGLE_vulkan_image"));
+ ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_vulkan_image"));
+
+ VulkanHelper helper;
+ helper.initializeFromANGLE();
constexpr GLuint kColor = 0xafbfcfdf;
@@ -188,16 +200,17 @@
EXPECT_EGL_TRUE(eglDestroyImageKHR(display, eglImage));
}
+// Check pixels after glClear
TEST_P(VulkanImageTest, PixelTestClear)
{
- ANGLE_SKIP_TEST_IF(!IsVulkan());
+ EGLWindow *window = getEGLWindow();
+ EGLDisplay display = window->getDisplay();
+
+ ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_vulkan_image"));
VulkanHelper helper;
helper.initializeFromANGLE();
- EGLWindow *window = getEGLWindow();
- EGLDisplay display = window->getDisplay();
-
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
@@ -256,16 +269,17 @@
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
+// Check pixels after GL draw.
TEST_P(VulkanImageTest, PixelTestDrawQuad)
{
- ANGLE_SKIP_TEST_IF(!IsVulkan());
+ EGLWindow *window = getEGLWindow();
+ EGLDisplay display = window->getDisplay();
+
+ ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_vulkan_image"));
VulkanHelper helper;
helper.initializeFromANGLE();
- EGLWindow *window = getEGLWindow();
- EGLDisplay display = window->getDisplay();
-
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
@@ -314,6 +328,99 @@
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
+// Test importing VkImage with eglCreateImageKHR
+TEST_P(VulkanImageTest, ClientBuffer)
+{
+ EGLWindow *window = getEGLWindow();
+ EGLDisplay display = window->getDisplay();
+
+ ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_vulkan_image"));
+
+ VulkanHelper helper;
+ helper.initializeFromANGLE();
+
+ constexpr VkImageUsageFlags kDefaultImageUsageFlags =
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
+
+ VkImage vkImage = VK_NULL_HANDLE;
+ VkDeviceMemory vkDeviceMemory = VK_NULL_HANDLE;
+ VkDeviceSize deviceSize = 0u;
+ VkImageCreateInfo imageCreateInfo = {};
+
+ VkResult result = VK_SUCCESS;
+ result = helper.createImage2D(VK_FORMAT_R8G8B8A8_UNORM, 0, kDefaultImageUsageFlags,
+ {kWidth, kHeight, 1}, &vkImage, &vkDeviceMemory, &deviceSize,
+ &imageCreateInfo);
+ EXPECT_EQ(result, VK_SUCCESS);
+ EXPECT_EQ(imageCreateInfo.sType, VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO);
+
+ uint64_t info = reinterpret_cast<uint64_t>(&imageCreateInfo);
+ EGLint attribs[] = {
+ EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE,
+ static_cast<EGLint>((info >> 32) & 0xffffffff),
+ EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE,
+ static_cast<EGLint>(info & 0xffffffff),
+ EGL_NONE,
+ };
+ EGLImageKHR eglImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_VULKAN_IMAGE_ANGLE,
+ reinterpret_cast<EGLClientBuffer>(&vkImage), attribs);
+ EXPECT_NE(eglImage, EGL_NO_IMAGE_KHR);
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage);
+
+ GLuint textures[1] = {texture};
+ GLenum layouts[1] = {GL_NONE};
+ glAcquireTexturesANGLE(1, textures, layouts);
+
+ GLFramebuffer framebuffer;
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+ EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glViewport(0, 0, kWidth, kHeight);
+ // clear framebuffer with white color.
+ glClearColor(1.f, 1.f, 1.f, 1.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ textures[0] = texture;
+ layouts[0] = GL_NONE;
+ glReleaseTexturesANGLE(1, textures, layouts);
+ EXPECT_EQ(layouts[0], static_cast<GLenum>(GL_LAYOUT_TRANSFER_DST_EXT));
+
+ std::vector<GLuint> pixels(kWidth * kHeight);
+ helper.readPixels(vkImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageCreateInfo.format, {},
+ imageCreateInfo.extent, pixels.data(), pixels.size() * sizeof(GLuint));
+ EXPECT_EQ(pixels, std::vector<GLuint>(kWidth * kHeight, kWhite));
+
+ layouts[0] = GL_LAYOUT_TRANSFER_SRC_EXT;
+ glAcquireTexturesANGLE(1, textures, layouts);
+
+ // clear framebuffer with red color.
+ glClearColor(1.f, 0.f, 0.f, 1.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glReleaseTexturesANGLE(1, textures, layouts);
+ EXPECT_EQ(layouts[0], static_cast<GLenum>(GL_LAYOUT_TRANSFER_DST_EXT));
+
+ helper.readPixels(vkImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageCreateInfo.format, {},
+ imageCreateInfo.extent, pixels.data(), pixels.size() * sizeof(GLuint));
+ EXPECT_EQ(pixels, std::vector<GLuint>(kWidth * kHeight, kRed));
+
+ EXPECT_GL_NO_ERROR();
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ framebuffer.reset();
+ texture.reset();
+
+ glFinish();
+
+ EXPECT_EGL_TRUE(eglDestroyImageKHR(display, eglImage));
+ vkDestroyImage(helper.getDevice(), vkImage, nullptr);
+ vkFreeMemory(helper.getDevice(), vkDeviceMemory, nullptr);
+}
// 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(VulkanImageTest);
diff --git a/src/tests/test_utils/VulkanHelper.cpp b/src/tests/test_utils/VulkanHelper.cpp
index fdae4bd..c87ced5 100644
--- a/src/tests/test_utils/VulkanHelper.cpp
+++ b/src/tests/test_utils/VulkanHelper.cpp
@@ -461,6 +461,80 @@
ASSERT(!mHasExternalSemaphoreFuchsia || vkGetSemaphoreZirconHandleFUCHSIA);
}
+VkResult VulkanHelper::createImage2D(VkFormat format,
+ VkImageCreateFlags createFlags,
+ VkImageUsageFlags usageFlags,
+ VkExtent3D extent,
+ VkImage *imageOut,
+ VkDeviceMemory *deviceMemoryOut,
+ VkDeviceSize *deviceMemorySizeOut,
+ VkImageCreateInfo *imageCreateInfoOut)
+{
+ VkImageCreateInfo imageCreateInfo = {
+ /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ /* .pNext = */ nullptr,
+ /* .flags = */ createFlags,
+ /* .imageType = */ VK_IMAGE_TYPE_2D,
+ /* .format = */ format,
+ /* .extent = */ extent,
+ /* .mipLevels = */ 1,
+ /* .arrayLayers = */ 1,
+ /* .samples = */ VK_SAMPLE_COUNT_1_BIT,
+ /* .tiling = */ VK_IMAGE_TILING_OPTIMAL,
+ /* .usage = */ usageFlags,
+ /* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE,
+ /* .queueFamilyIndexCount = */ 0,
+ /* .pQueueFamilyIndices = */ nullptr,
+ /* initialLayout = */ VK_IMAGE_LAYOUT_UNDEFINED,
+ };
+
+ VkImage image = VK_NULL_HANDLE;
+ VkResult result = vkCreateImage(mDevice, &imageCreateInfo, nullptr, &image);
+ if (result != VK_SUCCESS)
+ {
+ return result;
+ }
+
+ VkMemoryPropertyFlags requestedMemoryPropertyFlags = 0;
+ VkMemoryRequirements memoryRequirements;
+ vkGetImageMemoryRequirements(mDevice, image, &memoryRequirements);
+ uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits,
+ requestedMemoryPropertyFlags);
+ ASSERT(memoryTypeIndex != UINT32_MAX);
+ VkDeviceSize deviceMemorySize = memoryRequirements.size;
+
+ VkMemoryAllocateInfo memoryAllocateInfo = {
+ /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ /* .pNext = */ nullptr,
+ /* .allocationSize = */ deviceMemorySize,
+ /* .memoryTypeIndex = */ memoryTypeIndex,
+ };
+
+ VkDeviceMemory deviceMemory = VK_NULL_HANDLE;
+ result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory);
+ if (result != VK_SUCCESS)
+ {
+ vkDestroyImage(mDevice, image, nullptr);
+ return result;
+ }
+
+ VkDeviceSize memoryOffset = 0;
+ result = vkBindImageMemory(mDevice, image, deviceMemory, memoryOffset);
+ if (result != VK_SUCCESS)
+ {
+ vkFreeMemory(mDevice, deviceMemory, nullptr);
+ vkDestroyImage(mDevice, image, nullptr);
+ return result;
+ }
+
+ *imageOut = image;
+ *deviceMemoryOut = deviceMemory;
+ *deviceMemorySizeOut = deviceMemorySize;
+ *imageCreateInfoOut = imageCreateInfo;
+
+ return VK_SUCCESS;
+}
+
bool VulkanHelper::canCreateImageExternal(VkFormat format,
VkImageType type,
VkImageTiling tiling,
diff --git a/src/tests/test_utils/VulkanHelper.h b/src/tests/test_utils/VulkanHelper.h
index 4e3c26a..9177ab1 100644
--- a/src/tests/test_utils/VulkanHelper.h
+++ b/src/tests/test_utils/VulkanHelper.h
@@ -28,6 +28,14 @@
VkDevice getDevice() const { return mDevice; }
VkQueue getGraphicsQueue() const { return mGraphicsQueue; }
+ VkResult createImage2D(VkFormat format,
+ VkImageCreateFlags createFlags,
+ VkImageUsageFlags usageFlags,
+ VkExtent3D extent,
+ VkImage *imageOut,
+ VkDeviceMemory *deviceMemoryOut,
+ VkDeviceSize *deviceMemorySizeOut,
+ VkImageCreateInfo *imageCreateInfoOut);
bool canCreateImageExternal(VkFormat format,
VkImageType type,
VkImageTiling tiling,