// // Copyright 2019 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. // // VulkanExternalImageTest.cpp : Tests of images allocated externally using Vulkan. #include "test_utils/ANGLETest.h" #include "common/debug.h" #include "test_utils/VulkanHelper.h" #include "test_utils/gl_raii.h" namespace angle { namespace { constexpr int kInvalidFd = -1; 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; constexpr VkImageCreateFlags kDefaultImageCreateFlags = 0; constexpr VkImageUsageFlags kNoStorageImageUsageFlags = kDefaultImageUsageFlags & ~VK_IMAGE_USAGE_STORAGE_BIT; constexpr VkImageCreateFlags kMutableImageCreateFlags = kDefaultImageCreateFlags | VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; // List of VkFormat/internalformat combinations Chrome uses. // This is compiled from the maps in // components/viz/common/resources/resource_format_utils.cc. const struct ImageFormatPair { VkFormat vkFormat; GLenum internalFormat; const char *requiredExtension; } kChromeFormats[] = { {VK_FORMAT_R8G8B8A8_UNORM, GL_RGBA8_OES}, // RGBA_8888 {VK_FORMAT_B8G8R8A8_UNORM, GL_BGRA8_EXT}, // BGRA_8888 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, GL_RGBA4}, // RGBA_4444 {VK_FORMAT_R16G16B16A16_SFLOAT, GL_RGBA16F_EXT}, // RGBA_F16 {VK_FORMAT_R8_UNORM, GL_R8_EXT}, // RED_8 {VK_FORMAT_R5G6B5_UNORM_PACK16, GL_RGB565}, // RGB_565 {VK_FORMAT_R16_UNORM, GL_R16_EXT, "GL_EXT_texture_norm16"}, // R16_EXT {VK_FORMAT_A2B10G10R10_UNORM_PACK32, GL_RGB10_A2_EXT}, // RGBA_1010102 {VK_FORMAT_R8_UNORM, GL_ALPHA8_EXT}, // ALPHA_8 {VK_FORMAT_R8_UNORM, GL_LUMINANCE8_EXT}, // LUMINANCE_8 {VK_FORMAT_R8G8_UNORM, GL_RG8_EXT}, // RG_88 {VK_FORMAT_R8G8B8A8_UNORM, GL_RGB8_OES}, // RGBX_8888 }; struct OpaqueFdTraits { using Handle = int; static Handle InvalidHandle() { return kInvalidFd; } static const char *MemoryObjectExtension() { return "GL_EXT_memory_object_fd"; } static const char *SemaphoreExtension() { return "GL_EXT_semaphore_fd"; } static bool CanCreateSemaphore(const VulkanHelper &helper) { return helper.canCreateSemaphoreOpaqueFd(); } static VkResult CreateSemaphore(VulkanHelper *helper, VkSemaphore *semaphore) { return helper->createSemaphoreOpaqueFd(semaphore); } static VkResult ExportSemaphore(VulkanHelper *helper, VkSemaphore semaphore, Handle *handle) { return helper->exportSemaphoreOpaqueFd(semaphore, handle); } static void ImportSemaphore(GLuint semaphore, Handle handle) { glImportSemaphoreFdEXT(semaphore, GL_HANDLE_TYPE_OPAQUE_FD_EXT, handle); } static bool CanCreateImage(const VulkanHelper &helper, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageCreateFlags createFlags, VkImageUsageFlags usageFlags) { return helper.canCreateImageOpaqueFd(format, type, tiling, createFlags, usageFlags); } static VkResult CreateImage2D(VulkanHelper *helper, VkFormat format, VkImageCreateFlags createFlags, VkImageUsageFlags usageFlags, const void *imageCreateInfoPNext, VkExtent3D extent, VkImage *imageOut, VkDeviceMemory *deviceMemoryOut, VkDeviceSize *deviceMemorySizeOut) { return helper->createImage2DOpaqueFd(format, createFlags, usageFlags, imageCreateInfoPNext, extent, imageOut, deviceMemoryOut, deviceMemorySizeOut); } static VkResult ExportMemory(VulkanHelper *helper, VkDeviceMemory deviceMemory, Handle *handle) { return helper->exportMemoryOpaqueFd(deviceMemory, handle); } static void ImportMemory(GLuint memoryObject, GLuint64 size, Handle handle) { glImportMemoryFdEXT(memoryObject, size, GL_HANDLE_TYPE_OPAQUE_FD_EXT, handle); } }; struct FuchsiaTraits { using Handle = zx_handle_t; static Handle InvalidHandle() { return ZX_HANDLE_INVALID; } static const char *MemoryObjectExtension() { return "GL_ANGLE_memory_object_fuchsia"; } static const char *SemaphoreExtension() { return "GL_ANGLE_semaphore_fuchsia"; } static bool CanCreateSemaphore(const VulkanHelper &helper) { return helper.canCreateSemaphoreZirconEvent(); } static VkResult CreateSemaphore(VulkanHelper *helper, VkSemaphore *semaphore) { return helper->createSemaphoreZirconEvent(semaphore); } static VkResult ExportSemaphore(VulkanHelper *helper, VkSemaphore semaphore, Handle *handle) { return helper->exportSemaphoreZirconEvent(semaphore, handle); } static void ImportSemaphore(GLuint semaphore, Handle handle) { glImportSemaphoreZirconHandleANGLE(semaphore, GL_HANDLE_TYPE_ZIRCON_EVENT_ANGLE, handle); } static bool CanCreateImage(const VulkanHelper &helper, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageCreateFlags createFlags, VkImageUsageFlags usageFlags) { return helper.canCreateImageZirconVmo(format, type, tiling, createFlags, usageFlags); } static VkResult CreateImage2D(VulkanHelper *helper, VkFormat format, VkImageCreateFlags createFlags, VkImageUsageFlags usageFlags, const void *imageCreateInfoPNext, VkExtent3D extent, VkImage *imageOut, VkDeviceMemory *deviceMemoryOut, VkDeviceSize *deviceMemorySizeOut) { return helper->createImage2DZirconVmo(format, createFlags, usageFlags, imageCreateInfoPNext, extent, imageOut, deviceMemoryOut, deviceMemorySizeOut); } static VkResult ExportMemory(VulkanHelper *helper, VkDeviceMemory deviceMemory, Handle *handle) { return helper->exportMemoryZirconVmo(deviceMemory, handle); } static void ImportMemory(GLuint memoryObject, GLuint64 size, Handle handle) { glImportMemoryZirconHandleANGLE(memoryObject, size, GL_HANDLE_TYPE_ZIRCON_VMO_ANGLE, handle); } }; VkImageLayout GetPostReleaseVulkanLayout(GLenum glLayout) { switch (glLayout) { case GL_NONE: case GL_LAYOUT_GENERAL_EXT: default: return VK_IMAGE_LAYOUT_GENERAL; case GL_LAYOUT_COLOR_ATTACHMENT_EXT: return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; case GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; case GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; case GL_LAYOUT_SHADER_READ_ONLY_EXT: return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; case GL_LAYOUT_TRANSFER_SRC_EXT: return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; case GL_LAYOUT_TRANSFER_DST_EXT: return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; case GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT: return VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR; case GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT: return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR; } } void AdjustCreateFlags(bool useMemoryObjectFlags, VkImageCreateFlags *createFlags) { // If the GL_ANGLE_memory_object_flags extension is not supported, GL assumes that the mutable // create flag is specified. if (!useMemoryObjectFlags) { *createFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; } // The spec is not clear about the other create flags. } constexpr uint32_t kWidth = 64; constexpr uint32_t kHeight = 64; } // namespace class VulkanExternalImageTest : public ANGLETest<> { protected: VulkanExternalImageTest() { setWindowWidth(1); setWindowHeight(1); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); } template void runShouldDrawTest(bool isSwiftshader, bool enableDebugLayers); template void runWaitSemaphoresRetainsContentTest(bool isSwiftshader, bool enableDebugLayers); }; class VulkanExternalImageTestES31 : public VulkanExternalImageTest {}; template void RunShouldImportMemoryTest(VkImageCreateFlags createFlags, VkImageUsageFlags usageFlags, bool isSwiftshader, bool enableDebugLayers) { ASSERT(EnsureGLExtensionEnabled(Traits::MemoryObjectExtension())); VulkanHelper helper; helper.initialize(isSwiftshader, enableDebugLayers); VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; ANGLE_SKIP_TEST_IF(!Traits::CanCreateImage(helper, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, createFlags, usageFlags)); VkImage image = VK_NULL_HANDLE; VkDeviceMemory deviceMemory = VK_NULL_HANDLE; VkDeviceSize deviceMemorySize = 0; VkExtent3D extent = {1, 1, 1}; VkResult result = Traits::CreateImage2D(&helper, format, createFlags, usageFlags, nullptr, extent, &image, &deviceMemory, &deviceMemorySize); EXPECT_EQ(result, VK_SUCCESS); typename Traits::Handle memoryHandle = Traits::InvalidHandle(); result = Traits::ExportMemory(&helper, deviceMemory, &memoryHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(memoryHandle, Traits::InvalidHandle()); { GLMemoryObject memoryObject; GLint dedicatedMemory = GL_TRUE; glMemoryObjectParameterivEXT(memoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicatedMemory); Traits::ImportMemory(memoryObject, deviceMemorySize, memoryHandle); // Test that after calling glImportMemoryFdEXT, the parameters of the memory object cannot // be changed dedicatedMemory = GL_FALSE; glMemoryObjectParameterivEXT(memoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicatedMemory); EXPECT_GL_ERROR(GL_INVALID_OPERATION); } EXPECT_GL_NO_ERROR(); vkDestroyImage(helper.getDevice(), image, nullptr); vkFreeMemory(helper.getDevice(), deviceMemory, nullptr); } // glImportMemoryFdEXT must be able to import a valid opaque fd. TEST_P(VulkanExternalImageTest, ShouldImportMemoryOpaqueFd) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); RunShouldImportMemoryTest(kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // glImportMemoryZirconHandleANGLE must be able to import a valid vmo. TEST_P(VulkanExternalImageTest, ShouldImportMemoryZirconVmo) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); RunShouldImportMemoryTest(kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } template void RunShouldImportSemaphoreTest(bool isSwiftshader, bool enableDebugLayers) { ASSERT(EnsureGLExtensionEnabled(Traits::SemaphoreExtension())); VulkanHelper helper; helper.initialize(isSwiftshader, enableDebugLayers); ANGLE_SKIP_TEST_IF(!Traits::CanCreateSemaphore(helper)); VkSemaphore vkSemaphore = VK_NULL_HANDLE; VkResult result = helper.createSemaphoreOpaqueFd(&vkSemaphore); EXPECT_EQ(result, VK_SUCCESS); typename Traits::Handle semaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkSemaphore, &semaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(semaphoreHandle, Traits::InvalidHandle()); { GLSemaphore glSemaphore; Traits::ImportSemaphore(glSemaphore, semaphoreHandle); } EXPECT_GL_NO_ERROR(); vkDestroySemaphore(helper.getDevice(), vkSemaphore, nullptr); } // glImportSemaphoreFdEXT must be able to import a valid opaque fd. TEST_P(VulkanExternalImageTest, ShouldImportSemaphoreOpaqueFd) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); RunShouldImportSemaphoreTest(isSwiftshader(), enableDebugLayers()); } // glImportSemaphoreZirconHandleANGLE must be able to import a valid handle. TEST_P(VulkanExternalImageTest, ShouldImportSemaphoreZirconEvent) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_semaphore_fuchsia")); RunShouldImportSemaphoreTest(isSwiftshader(), enableDebugLayers()); } template void RunShouldClearTest(bool useMemoryObjectFlags, VkImageCreateFlags createFlags, VkImageUsageFlags usageFlags, bool isSwiftshader, bool enableDebugLayers) { ASSERT(EnsureGLExtensionEnabled(Traits::MemoryObjectExtension())); VulkanHelper helper; helper.initialize(isSwiftshader, enableDebugLayers); AdjustCreateFlags(useMemoryObjectFlags, &createFlags); VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; ANGLE_SKIP_TEST_IF(!Traits::CanCreateImage(helper, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, createFlags, usageFlags)); VkImage image = VK_NULL_HANDLE; VkDeviceMemory deviceMemory = VK_NULL_HANDLE; VkDeviceSize deviceMemorySize = 0; VkExtent3D extent = {1, 1, 1}; VkResult result = Traits::CreateImage2D(&helper, format, createFlags, usageFlags, nullptr, extent, &image, &deviceMemory, &deviceMemorySize); EXPECT_EQ(result, VK_SUCCESS); typename Traits::Handle memoryHandle = Traits::InvalidHandle(); result = Traits::ExportMemory(&helper, deviceMemory, &memoryHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(memoryHandle, Traits::InvalidHandle()); { GLMemoryObject memoryObject; GLint dedicatedMemory = GL_TRUE; glMemoryObjectParameterivEXT(memoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicatedMemory); Traits::ImportMemory(memoryObject, deviceMemorySize, memoryHandle); GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); if (useMemoryObjectFlags) { glTexStorageMemFlags2DANGLE(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1, memoryObject, 0, createFlags, usageFlags, nullptr); } else { glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1, memoryObject, 0); } GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); glClearColor(0.5f, 0.5f, 0.5f, 0.5f); glClear(GL_COLOR_BUFFER_BIT); EXPECT_PIXEL_NEAR(0, 0, 128, 128, 128, 128, 1.0); } EXPECT_GL_NO_ERROR(); vkDestroyImage(helper.getDevice(), image, nullptr); vkFreeMemory(helper.getDevice(), deviceMemory, nullptr); } // Test creating and clearing a simple RGBA8 texture in an opaque fd. TEST_P(VulkanExternalImageTest, ShouldClearOpaqueFdRGBA8) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); // http://anglebug.com/42263236 ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL() && (IsPixel2() || IsPixel2XL())); RunShouldClearTest(false, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing a simple RGBA8 texture in an opaque fd, using // GL_ANGLE_memory_object_flags. TEST_P(VulkanExternalImageTest, ShouldClearOpaqueWithFlagsFdRGBA8) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunShouldClearTest(true, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing a simple RGBA8 texture without STORAGE usage in an opaque fd. TEST_P(VulkanExternalImageTest, ShouldClearNoStorageUsageOpaqueFdRGBA8) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunShouldClearTest(true, kDefaultImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing a simple RGBA8 texture without STORAGE usage but with MUTABLE in an // opaque fd. TEST_P(VulkanExternalImageTest, ShouldClearMutableNoStorageUsageOpaqueFdRGBA8) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunShouldClearTest(true, kMutableImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing a simple RGBA8 texture in a zircon vmo. TEST_P(VulkanExternalImageTest, ShouldClearZirconVmoRGBA8) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); RunShouldClearTest(false, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing a simple RGBA8 texture in a zircon vmo, using // GL_ANGLE_memory_object_flags. TEST_P(VulkanExternalImageTest, ShouldClearZirconWithFlagsVmoRGBA8) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunShouldClearTest(true, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing a simple RGBA8 texture without STORAGE usage in a zircon vmo. TEST_P(VulkanExternalImageTest, ShouldClearNoStorageUsageZirconVmoRGBA8) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunShouldClearTest(true, kDefaultImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing a simple RGBA8 texture without STORAGE usage but with MUTABLE in a // zircon vmo. TEST_P(VulkanExternalImageTest, ShouldClearMutableNoStorageUsageZirconVmoRGBA8) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunShouldClearTest(true, kMutableImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers()); } template void RunTextureFormatCompatChromiumTest(bool useMemoryObjectFlags, VkImageCreateFlags createFlags, VkImageUsageFlags usageFlags, bool isSwiftshader, bool enableDebugLayers, bool isES3) { ASSERT(EnsureGLExtensionEnabled(Traits::MemoryObjectExtension())); AdjustCreateFlags(useMemoryObjectFlags, &createFlags); VulkanHelper helper; helper.initialize(isSwiftshader, enableDebugLayers); for (const ImageFormatPair &format : kChromeFormats) { // https://crbug.com/angleproject/5046 if ((format.vkFormat == VK_FORMAT_R4G4B4A4_UNORM_PACK16) && IsIntel()) { continue; } if (!Traits::CanCreateImage(helper, format.vkFormat, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, createFlags, usageFlags)) { continue; } if (format.requiredExtension && !IsGLExtensionEnabled(format.requiredExtension)) { continue; } if (format.internalFormat == GL_RGB10_A2_EXT && !isES3 && !IsGLExtensionEnabled("GL_EXT_texture_type_2_10_10_10_REV")) { continue; } VkImage image = VK_NULL_HANDLE; VkDeviceMemory deviceMemory = VK_NULL_HANDLE; VkDeviceSize deviceMemorySize = 0; VkExtent3D extent = {113, 211, 1}; VkResult result = Traits::CreateImage2D(&helper, format.vkFormat, createFlags, usageFlags, nullptr, extent, &image, &deviceMemory, &deviceMemorySize); EXPECT_EQ(result, VK_SUCCESS); typename Traits::Handle memoryHandle = Traits::InvalidHandle(); result = Traits::ExportMemory(&helper, deviceMemory, &memoryHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(memoryHandle, Traits::InvalidHandle()); { GLMemoryObject memoryObject; GLint dedicatedMemory = GL_TRUE; glMemoryObjectParameterivEXT(memoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicatedMemory); Traits::ImportMemory(memoryObject, deviceMemorySize, memoryHandle); GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); if (useMemoryObjectFlags) { glTexStorageMemFlags2DANGLE(GL_TEXTURE_2D, 1, format.internalFormat, extent.width, extent.height, memoryObject, 0, createFlags, usageFlags, nullptr); } else { glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, format.internalFormat, extent.width, extent.height, memoryObject, 0); } } EXPECT_GL_NO_ERROR(); vkDestroyImage(helper.getDevice(), image, nullptr); vkFreeMemory(helper.getDevice(), deviceMemory, nullptr); } } // Test all format combinations used by Chrome import successfully (opaque fd). TEST_P(VulkanExternalImageTest, TextureFormatCompatChromiumFd) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); RunTextureFormatCompatChromiumTest( false, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers(), getClientMajorVersion() >= 3); } // Test all format combinations used by Chrome import successfully (opaque fd), using // GL_ANGLE_memory_object_flags. TEST_P(VulkanExternalImageTest, TextureFormatCompatChromiumWithFlagsFd) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunTextureFormatCompatChromiumTest( true, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers(), getClientMajorVersion() >= 3); } // Test all format combinations used by Chrome import successfully (opaque fd), without STORAGE // usage. TEST_P(VulkanExternalImageTest, TextureFormatCompatChromiumNoStorageFd) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunTextureFormatCompatChromiumTest( true, kDefaultImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers(), getClientMajorVersion() >= 3); } // Test all format combinations used by Chrome import successfully (opaque fd), without STORAGE // usage but with MUTABLE. TEST_P(VulkanExternalImageTest, TextureFormatCompatChromiumMutableNoStorageFd) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); // http://anglebug.com/42264218 ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan()); RunTextureFormatCompatChromiumTest( true, kMutableImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers(), getClientMajorVersion() >= 3); } // Test all format combinations used by Chrome import successfully (fuchsia). TEST_P(VulkanExternalImageTest, TextureFormatCompatChromiumZirconVmo) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); RunTextureFormatCompatChromiumTest( false, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers(), getClientMajorVersion() >= 3); } // Test all format combinations used by Chrome import successfully (fuchsia), using // GL_ANGLE_memory_object_flags. TEST_P(VulkanExternalImageTest, TextureFormatCompatChromiumWithFlagsZirconVmo) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunTextureFormatCompatChromiumTest( true, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers(), getClientMajorVersion() >= 3); } // Test all format combinations used by Chrome import successfully (fuchsia), without STORAGE usage. TEST_P(VulkanExternalImageTest, TextureFormatCompatChromiumNoStorageZirconVmo) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunTextureFormatCompatChromiumTest( true, kDefaultImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers(), getClientMajorVersion() >= 3); } // Test all format combinations used by Chrome import successfully (fuchsia), without STORAGE usage // but with MUTABLE. TEST_P(VulkanExternalImageTest, TextureFormatCompatChromiumMutableNoStorageZirconVmo) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunTextureFormatCompatChromiumTest( true, kMutableImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers(), getClientMajorVersion() >= 3); } template void RunShouldClearWithSemaphoresTest(bool useMemoryObjectFlags, VkImageCreateFlags createFlags, VkImageUsageFlags usageFlags, bool isSwiftshader, bool enableDebugLayers) { ASSERT(EnsureGLExtensionEnabled(Traits::MemoryObjectExtension())); ASSERT(EnsureGLExtensionEnabled(Traits::SemaphoreExtension())); AdjustCreateFlags(useMemoryObjectFlags, &createFlags); VulkanHelper helper; helper.initialize(isSwiftshader, enableDebugLayers); VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; ANGLE_SKIP_TEST_IF(!Traits::CanCreateImage(helper, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, createFlags, usageFlags)); ANGLE_SKIP_TEST_IF(!Traits::CanCreateSemaphore(helper)); VkSemaphore vkAcquireSemaphore = VK_NULL_HANDLE; VkResult result = Traits::CreateSemaphore(&helper, &vkAcquireSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkAcquireSemaphore != VK_NULL_HANDLE); VkSemaphore vkReleaseSemaphore = VK_NULL_HANDLE; result = Traits::CreateSemaphore(&helper, &vkReleaseSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkReleaseSemaphore != VK_NULL_HANDLE); typename Traits::Handle acquireSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkAcquireSemaphore, &acquireSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(acquireSemaphoreHandle, Traits::InvalidHandle()); typename Traits::Handle releaseSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkReleaseSemaphore, &releaseSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(releaseSemaphoreHandle, Traits::InvalidHandle()); VkImage image = VK_NULL_HANDLE; VkDeviceMemory deviceMemory = VK_NULL_HANDLE; VkDeviceSize deviceMemorySize = 0; VkExtent3D extent = {1, 1, 1}; result = Traits::CreateImage2D(&helper, format, createFlags, usageFlags, nullptr, extent, &image, &deviceMemory, &deviceMemorySize); EXPECT_EQ(result, VK_SUCCESS); typename Traits::Handle memoryHandle = Traits::InvalidHandle(); result = Traits::ExportMemory(&helper, deviceMemory, &memoryHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(memoryHandle, Traits::InvalidHandle()); { GLMemoryObject memoryObject; GLint dedicatedMemory = GL_TRUE; glMemoryObjectParameterivEXT(memoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicatedMemory); Traits::ImportMemory(memoryObject, deviceMemorySize, memoryHandle); GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); if (useMemoryObjectFlags) { glTexStorageMemFlags2DANGLE(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1, memoryObject, 0, createFlags, usageFlags, nullptr); } else { glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1, memoryObject, 0); } GLSemaphore glAcquireSemaphore; Traits::ImportSemaphore(glAcquireSemaphore, acquireSemaphoreHandle); helper.releaseImageAndSignalSemaphore(image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, vkAcquireSemaphore); const GLuint barrierTextures[] = { texture, }; constexpr uint32_t textureBarriersCount = std::extent(); const GLenum textureSrcLayouts[] = { GL_LAYOUT_GENERAL_EXT, }; constexpr uint32_t textureSrcLayoutsCount = std::extent(); static_assert(textureBarriersCount == textureSrcLayoutsCount, "barrierTextures and textureSrcLayouts must be the same length"); glWaitSemaphoreEXT(glAcquireSemaphore, 0, nullptr, textureBarriersCount, barrierTextures, textureSrcLayouts); GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); glClearColor(0.5f, 0.5f, 0.5f, 0.5f); glClear(GL_COLOR_BUFFER_BIT); GLSemaphore glReleaseSemaphore; Traits::ImportSemaphore(glReleaseSemaphore, releaseSemaphoreHandle); const GLenum textureDstLayouts[] = { GL_LAYOUT_TRANSFER_SRC_EXT, }; constexpr uint32_t textureDstLayoutsCount = std::extent(); static_assert(textureBarriersCount == textureDstLayoutsCount, "barrierTextures and textureDstLayouts must be the same length"); glSignalSemaphoreEXT(glReleaseSemaphore, 0, nullptr, textureBarriersCount, barrierTextures, textureDstLayouts); helper.waitSemaphoreAndAcquireImage(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vkReleaseSemaphore); uint8_t pixels[4]; VkOffset3D offset = {}; helper.readPixels(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, format, offset, extent, pixels, sizeof(pixels)); EXPECT_NEAR(0x80, pixels[0], 1); EXPECT_NEAR(0x80, pixels[1], 1); EXPECT_NEAR(0x80, pixels[2], 1); EXPECT_NEAR(0x80, pixels[3], 1); } EXPECT_GL_NO_ERROR(); vkDeviceWaitIdle(helper.getDevice()); vkDestroyImage(helper.getDevice(), image, nullptr); vkDestroySemaphore(helper.getDevice(), vkAcquireSemaphore, nullptr); vkDestroySemaphore(helper.getDevice(), vkReleaseSemaphore, nullptr); vkFreeMemory(helper.getDevice(), deviceMemory, nullptr); } // Test creating and clearing RGBA8 texture in opaque fd with acquire/release. TEST_P(VulkanExternalImageTest, ShouldClearOpaqueFdWithSemaphores) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); // http://issuetracker.google.com/173004081 ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsLinux() && getEGLWindow()->isFeatureEnabled(Feature::AsyncCommandQueue)); // http://anglebug.com/42263923 ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsDesktopOpenGL()); RunShouldClearWithSemaphoresTest(false, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing RGBA8 texture in opaque fd with acquire/release, using // GL_ANGLE_memory_object_flags. TEST_P(VulkanExternalImageTest, ShouldClearOpaqueFdWithSemaphoresWithFlags) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); // http://issuetracker.google.com/173004081 ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsLinux() && getEGLWindow()->isFeatureEnabled(Feature::AsyncCommandQueue)); RunShouldClearWithSemaphoresTest(true, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing RGBA8 texture without STORAGE usage in opaque fd with acquire/release. TEST_P(VulkanExternalImageTest, ShouldClearOpaqueFdWithSemaphoresNoStorage) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); // http://issuetracker.google.com/173004081 ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsLinux() && getEGLWindow()->isFeatureEnabled(Feature::AsyncCommandQueue)); RunShouldClearWithSemaphoresTest(true, kDefaultImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing RGBA8 texture without STORAGE usage but with MUTABLE in opaque fd with // acquire/release. TEST_P(VulkanExternalImageTest, ShouldClearOpaqueFdWithSemaphoresMutableNoStorage) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); // http://issuetracker.google.com/173004081 ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsLinux() && getEGLWindow()->isFeatureEnabled(Feature::AsyncCommandQueue)); RunShouldClearWithSemaphoresTest(true, kMutableImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing RGBA8 texture in zircon vmo with acquire/release. TEST_P(VulkanExternalImageTest, ShouldClearZirconVmoWithSemaphores) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_semaphore_fuchsia")); RunShouldClearWithSemaphoresTest(false, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing RGBA8 texture in zircon vmo with acquire/release, using // GL_ANGLE_memory_object_flags. TEST_P(VulkanExternalImageTest, ShouldClearZirconVmoWithSemaphoresWithFlags) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_semaphore_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunShouldClearWithSemaphoresTest(true, kDefaultImageCreateFlags, kDefaultImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing RGBA8 texture without STORAGE usage in zircon vmo with // acquire/release. TEST_P(VulkanExternalImageTest, ShouldClearZirconVmoWithSemaphoresNoStorage) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_semaphore_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunShouldClearWithSemaphoresTest(true, kDefaultImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers()); } // Test creating and clearing RGBA8 texture without STORAGE usage but with MUTABLE in zircon vmo // with acquire/release. TEST_P(VulkanExternalImageTest, ShouldClearZirconVmoWithSemaphoresMutableNoStorage) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_semaphore_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunShouldClearWithSemaphoresTest(true, kMutableImageCreateFlags, kNoStorageImageUsageFlags, isSwiftshader(), enableDebugLayers()); } template void VulkanExternalImageTest::runShouldDrawTest(bool isSwiftshader, bool enableDebugLayers) { ASSERT(EnsureGLExtensionEnabled(Traits::MemoryObjectExtension())); ASSERT(EnsureGLExtensionEnabled(Traits::SemaphoreExtension())); VulkanHelper helper; helper.initialize(isSwiftshader, enableDebugLayers); VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; ANGLE_SKIP_TEST_IF(!Traits::CanCreateImage(helper, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, kDefaultImageCreateFlags, kDefaultImageUsageFlags)); ANGLE_SKIP_TEST_IF(!Traits::CanCreateSemaphore(helper)); VkSemaphore vkAcquireSemaphore = VK_NULL_HANDLE; VkResult result = Traits::CreateSemaphore(&helper, &vkAcquireSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkAcquireSemaphore != VK_NULL_HANDLE); VkSemaphore vkReleaseSemaphore = VK_NULL_HANDLE; result = Traits::CreateSemaphore(&helper, &vkReleaseSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkReleaseSemaphore != VK_NULL_HANDLE); typename Traits::Handle acquireSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkAcquireSemaphore, &acquireSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(acquireSemaphoreHandle, Traits::InvalidHandle()); typename Traits::Handle releaseSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkReleaseSemaphore, &releaseSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(releaseSemaphoreHandle, Traits::InvalidHandle()); VkImage image = VK_NULL_HANDLE; VkDeviceMemory deviceMemory = VK_NULL_HANDLE; VkDeviceSize deviceMemorySize = 0; VkExtent3D extent = {1, 1, 1}; result = Traits::CreateImage2D(&helper, format, kDefaultImageCreateFlags, kDefaultImageUsageFlags, nullptr, extent, &image, &deviceMemory, &deviceMemorySize); EXPECT_EQ(result, VK_SUCCESS); typename Traits::Handle memoryHandle = Traits::InvalidHandle(); result = Traits::ExportMemory(&helper, deviceMemory, &memoryHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(memoryHandle, Traits::InvalidHandle()); { GLMemoryObject memoryObject; GLint dedicatedMemory = GL_TRUE; glMemoryObjectParameterivEXT(memoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicatedMemory); Traits::ImportMemory(memoryObject, deviceMemorySize, memoryHandle); GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1, memoryObject, 0); GLSemaphore glAcquireSemaphore; Traits::ImportSemaphore(glAcquireSemaphore, acquireSemaphoreHandle); // Transfer ownership to GL. helper.releaseImageAndSignalSemaphore(image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, vkAcquireSemaphore); const GLuint barrierTextures[] = { texture, }; constexpr uint32_t textureBarriersCount = std::extent(); const GLenum textureSrcLayouts[] = { GL_LAYOUT_GENERAL_EXT, }; constexpr uint32_t textureSrcLayoutsCount = std::extent(); static_assert(textureBarriersCount == textureSrcLayoutsCount, "barrierTextures and textureSrcLayouts must be the same length"); glWaitSemaphoreEXT(glAcquireSemaphore, 0, nullptr, textureBarriersCount, barrierTextures, textureSrcLayouts); GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); // Make the texture red. ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f); EXPECT_GL_NO_ERROR(); // Transfer ownership back to test. GLSemaphore glReleaseSemaphore; Traits::ImportSemaphore(glReleaseSemaphore, releaseSemaphoreHandle); const GLenum textureDstLayouts[] = { GL_LAYOUT_TRANSFER_SRC_EXT, }; constexpr uint32_t textureDstLayoutsCount = std::extent(); static_assert(textureBarriersCount == textureDstLayoutsCount, "barrierTextures and textureDstLayouts must be the same length"); glSignalSemaphoreEXT(glReleaseSemaphore, 0, nullptr, textureBarriersCount, barrierTextures, textureDstLayouts); helper.waitSemaphoreAndAcquireImage(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vkReleaseSemaphore); uint8_t pixels[4]; VkOffset3D offset = {}; helper.readPixels(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, format, offset, extent, pixels, sizeof(pixels)); EXPECT_EQ(0xFF, pixels[0]); EXPECT_EQ(0x00, pixels[1]); EXPECT_EQ(0x00, pixels[2]); EXPECT_EQ(0xFF, pixels[3]); } EXPECT_GL_NO_ERROR(); vkDeviceWaitIdle(helper.getDevice()); vkDestroyImage(helper.getDevice(), image, nullptr); vkDestroySemaphore(helper.getDevice(), vkAcquireSemaphore, nullptr); vkDestroySemaphore(helper.getDevice(), vkReleaseSemaphore, nullptr); vkFreeMemory(helper.getDevice(), deviceMemory, nullptr); } // Test drawing to RGBA8 texture in opaque fd with acquire/release. TEST_P(VulkanExternalImageTest, ShouldDrawOpaqueFdWithSemaphores) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); // http://issuetracker.google.com/173004081 ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsLinux() && getEGLWindow()->isFeatureEnabled(Feature::AsyncCommandQueue)); // http://anglebug.com/42263923 ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsDesktopOpenGL()); runShouldDrawTest(isSwiftshader(), enableDebugLayers()); } // Test drawing to RGBA8 texture in zircon vmo with acquire/release multiple times. TEST_P(VulkanExternalImageTest, ShouldDrawZirconVmoWithSemaphores) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_semaphore_fuchsia")); runShouldDrawTest(isSwiftshader(), enableDebugLayers()); } template void VulkanExternalImageTest::runWaitSemaphoresRetainsContentTest(bool isSwiftshader, bool enableDebugLayers) { ASSERT(EnsureGLExtensionEnabled(Traits::MemoryObjectExtension())); ASSERT(EnsureGLExtensionEnabled(Traits::SemaphoreExtension())); VulkanHelper helper; helper.initialize(isSwiftshader, enableDebugLayers); VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; ANGLE_SKIP_TEST_IF(!Traits::CanCreateImage(helper, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, kDefaultImageCreateFlags, kDefaultImageUsageFlags)); ANGLE_SKIP_TEST_IF(!Traits::CanCreateSemaphore(helper)); VkSemaphore vkAcquireSemaphore = VK_NULL_HANDLE; VkResult result = Traits::CreateSemaphore(&helper, &vkAcquireSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkAcquireSemaphore != VK_NULL_HANDLE); VkSemaphore vkReleaseSemaphore = VK_NULL_HANDLE; result = Traits::CreateSemaphore(&helper, &vkReleaseSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkReleaseSemaphore != VK_NULL_HANDLE); typename Traits::Handle acquireSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkAcquireSemaphore, &acquireSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(acquireSemaphoreHandle, Traits::InvalidHandle()); typename Traits::Handle releaseSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkReleaseSemaphore, &releaseSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(releaseSemaphoreHandle, Traits::InvalidHandle()); VkImage image = VK_NULL_HANDLE; VkDeviceMemory deviceMemory = VK_NULL_HANDLE; VkDeviceSize deviceMemorySize = 0; VkExtent3D extent = {kWidth, kHeight, 1}; result = Traits::CreateImage2D(&helper, format, kDefaultImageCreateFlags, kDefaultImageUsageFlags, nullptr, extent, &image, &deviceMemory, &deviceMemorySize); EXPECT_EQ(result, VK_SUCCESS); typename Traits::Handle memoryHandle = Traits::InvalidHandle(); result = Traits::ExportMemory(&helper, deviceMemory, &memoryHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(memoryHandle, Traits::InvalidHandle()); { GLMemoryObject memoryObject; GLint dedicatedMemory = GL_TRUE; glMemoryObjectParameterivEXT(memoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicatedMemory); Traits::ImportMemory(memoryObject, deviceMemorySize, memoryHandle); GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight, memoryObject, 0); GLSemaphore glAcquireSemaphore; Traits::ImportSemaphore(glAcquireSemaphore, acquireSemaphoreHandle); // Transfer ownership to GL. helper.releaseImageAndSignalSemaphore(image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, vkAcquireSemaphore); const GLuint barrierTextures[] = { texture, }; constexpr uint32_t textureBarriersCount = std::extent(); const GLenum textureSrcLayouts[] = { GL_LAYOUT_GENERAL_EXT, }; constexpr uint32_t textureSrcLayoutsCount = std::extent(); static_assert(textureBarriersCount == textureSrcLayoutsCount, "barrierTextures and textureSrcLayouts must be the same length"); glWaitSemaphoreEXT(glAcquireSemaphore, 0, nullptr, textureBarriersCount, barrierTextures, textureSrcLayouts); GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); // Make the texture red. ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); glViewport(0, 0, kWidth, kHeight); drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f); EXPECT_GL_NO_ERROR(); // Transfer ownership back to test. GLSemaphore glReleaseSemaphore; Traits::ImportSemaphore(glReleaseSemaphore, releaseSemaphoreHandle); const GLenum textureDstLayouts[] = { GL_LAYOUT_TRANSFER_SRC_EXT, }; constexpr uint32_t textureDstLayoutsCount = std::extent(); static_assert(textureBarriersCount == textureDstLayoutsCount, "barrierTextures and textureDstLayouts must be the same length"); glSignalSemaphoreEXT(glReleaseSemaphore, 0, nullptr, textureBarriersCount, barrierTextures, textureDstLayouts); helper.waitSemaphoreAndAcquireImage(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vkReleaseSemaphore); // Transfer ownership to GL again, and make sure the contents are preserved. helper.releaseImageAndSignalSemaphore(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, vkAcquireSemaphore); glWaitSemaphoreEXT(glAcquireSemaphore, 0, nullptr, textureBarriersCount, barrierTextures, textureSrcLayouts); // Blend green ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.0f); // Transfer ownership back to test glSignalSemaphoreEXT(glReleaseSemaphore, 0, nullptr, textureBarriersCount, barrierTextures, textureDstLayouts); helper.waitSemaphoreAndAcquireImage(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vkReleaseSemaphore); uint8_t pixels[4 * kWidth * kHeight]; VkOffset3D offset = {}; helper.readPixels(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, format, offset, extent, pixels, sizeof(pixels)); EXPECT_EQ(0xFF, pixels[0]); EXPECT_EQ(0xFF, pixels[1]); EXPECT_EQ(0x00, pixels[2]); EXPECT_EQ(0xFF, pixels[3]); EXPECT_EQ(0xFF, pixels[4]); EXPECT_EQ(0xFF, pixels[5]); EXPECT_EQ(0x00, pixels[6]); EXPECT_EQ(0xFF, pixels[7]); } EXPECT_GL_NO_ERROR(); vkDeviceWaitIdle(helper.getDevice()); vkDestroyImage(helper.getDevice(), image, nullptr); vkDestroySemaphore(helper.getDevice(), vkAcquireSemaphore, nullptr); vkDestroySemaphore(helper.getDevice(), vkReleaseSemaphore, nullptr); vkFreeMemory(helper.getDevice(), deviceMemory, nullptr); } // Test drawing to RGBA8 texture in opaque fd with acquire/release multiple times. TEST_P(VulkanExternalImageTest, WaitSemaphoresRetainsContentOpaqueFd) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); // http://issuetracker.google.com/173004081 ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsLinux() && getEGLWindow()->isFeatureEnabled(Feature::AsyncCommandQueue)); // http://anglebug.com/42263923 ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsDesktopOpenGL()); runWaitSemaphoresRetainsContentTest(isSwiftshader(), enableDebugLayers()); } // Test drawing to RGBA8 texture in zircon vmo with acquire/release multiple times. TEST_P(VulkanExternalImageTest, WaitSemaphoresRetainsContentZirconVmo) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_semaphore_fuchsia")); runWaitSemaphoresRetainsContentTest(isSwiftshader(), enableDebugLayers()); } // Support for Zircon handle types is mandatory on Fuchsia. TEST_P(VulkanExternalImageTest, ShouldSupportExternalHandlesFuchsia) { ANGLE_SKIP_TEST_IF(!IsFuchsia()); EXPECT_TRUE(EnsureGLExtensionEnabled("GL_ANGLE_memory_object_fuchsia")); EXPECT_TRUE(EnsureGLExtensionEnabled("GL_ANGLE_semaphore_fuchsia")); VulkanHelper helper; helper.initialize(isSwiftshader(), enableDebugLayers()); EXPECT_TRUE(helper.canCreateSemaphoreZirconEvent()); EXPECT_TRUE(helper.canCreateImageZirconVmo(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, kDefaultImageCreateFlags, kDefaultImageUsageFlags)); } template void RunPreInitializedOnGLImportTest(bool useMemoryObjectFlags, VkImageTiling tiling, bool isSwiftshader, bool enableDebugLayers) { ASSERT(EnsureGLExtensionEnabled(Traits::MemoryObjectExtension())); ASSERT(EnsureGLExtensionEnabled(Traits::SemaphoreExtension())); VkImageCreateFlags createFlags = kDefaultImageCreateFlags; VkImageUsageFlags usageFlags = kDefaultImageUsageFlags; AdjustCreateFlags(useMemoryObjectFlags, &createFlags); VulkanHelper helper; helper.initialize(isSwiftshader, enableDebugLayers); VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; ANGLE_SKIP_TEST_IF( !Traits::CanCreateImage(helper, format, VK_IMAGE_TYPE_2D, tiling, createFlags, usageFlags)); ANGLE_SKIP_TEST_IF(!Traits::CanCreateSemaphore(helper)); VkSemaphore vkAcquireSemaphore = VK_NULL_HANDLE; VkResult result = Traits::CreateSemaphore(&helper, &vkAcquireSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkAcquireSemaphore != VK_NULL_HANDLE); VkSemaphore vkReleaseSemaphore = VK_NULL_HANDLE; result = Traits::CreateSemaphore(&helper, &vkReleaseSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkReleaseSemaphore != VK_NULL_HANDLE); typename Traits::Handle acquireSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkAcquireSemaphore, &acquireSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(acquireSemaphoreHandle, Traits::InvalidHandle()); typename Traits::Handle releaseSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkReleaseSemaphore, &releaseSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(releaseSemaphoreHandle, Traits::InvalidHandle()); VkImage image = VK_NULL_HANDLE; VkDeviceMemory deviceMemory = VK_NULL_HANDLE; VkDeviceSize deviceMemorySize = 0; VkExtent3D extent = {kWidth, kHeight, 1}; result = Traits::CreateImage2D(&helper, format, createFlags, usageFlags, nullptr, extent, &image, &deviceMemory, &deviceMemorySize); EXPECT_EQ(result, VK_SUCCESS); // Initialize a pixel in the image constexpr uint32_t kPixel = 0x12345678; helper.writePixels(image, VK_IMAGE_LAYOUT_UNDEFINED, VK_FORMAT_R8G8B8A8_UNORM, {0, 0, 0}, {1, 1, 1}, static_cast(&kPixel), sizeof(kPixel)); typename Traits::Handle memoryHandle = Traits::InvalidHandle(); result = Traits::ExportMemory(&helper, deviceMemory, &memoryHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(memoryHandle, Traits::InvalidHandle()); { GLMemoryObject memoryObject; GLint dedicatedMemory = GL_TRUE; glMemoryObjectParameterivEXT(memoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicatedMemory); Traits::ImportMemory(memoryObject, deviceMemorySize, memoryHandle); GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (tiling == VK_IMAGE_TILING_LINEAR) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_TILING_EXT, GL_LINEAR_TILING_EXT); } if (useMemoryObjectFlags) { glTexStorageMemFlags2DANGLE(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight, memoryObject, 0, createFlags, usageFlags, nullptr); } else { glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight, memoryObject, 0); } GLSemaphore glAcquireSemaphore; Traits::ImportSemaphore(glAcquireSemaphore, acquireSemaphoreHandle); // Note: writePixels leaves the image in TRANSFER_DST_OPTIMAL layout. helper.releaseImageAndSignalSemaphore(image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, vkAcquireSemaphore); const GLuint barrierTexture = texture; const GLenum textureSrcLayout = GL_LAYOUT_COLOR_ATTACHMENT_EXT; glWaitSemaphoreEXT(glAcquireSemaphore, 0, nullptr, 1, &barrierTexture, &textureSrcLayout); GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); // Readback the initialized pixel, ensure it contains the value written to it. uint32_t pixel = 0u; glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); EXPECT_EQ(pixel, kPixel); EXPECT_GL_NO_ERROR(); GLSemaphore glReleaseSemaphore; Traits::ImportSemaphore(glReleaseSemaphore, releaseSemaphoreHandle); const GLenum textureDstLayout = GL_LAYOUT_TRANSFER_SRC_EXT; glSignalSemaphoreEXT(glReleaseSemaphore, 0, nullptr, 1, &barrierTexture, &textureDstLayout); helper.waitSemaphoreAndAcquireImage(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vkReleaseSemaphore); } EXPECT_GL_NO_ERROR(); vkDeviceWaitIdle(helper.getDevice()); vkDestroyImage(helper.getDevice(), image, nullptr); vkDestroySemaphore(helper.getDevice(), vkAcquireSemaphore, nullptr); vkDestroySemaphore(helper.getDevice(), vkReleaseSemaphore, nullptr); vkFreeMemory(helper.getDevice(), deviceMemory, nullptr); } // Test that texture storage created from VkImage memory can be considered pre-initialized in GL. TEST_P(VulkanExternalImageTest, PreInitializedOnGLImport) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); RunPreInitializedOnGLImportTest(false, VK_IMAGE_TILING_OPTIMAL, isSwiftshader(), enableDebugLayers()); } // Test that texture storage created from VkImage memory can be considered pre-initialized in GL. // Uses linear tiling. TEST_P(VulkanExternalImageTest, PreInitializedOnGLImportLinear) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); RunPreInitializedOnGLImportTest(false, VK_IMAGE_TILING_LINEAR, isSwiftshader(), enableDebugLayers()); } // Test that texture storage created from VkImage memory can be considered pre-initialized in GL, // using GL_ANGLE_memory_object_flags. TEST_P(VulkanExternalImageTest, PreInitializedOnGLImportWithFlags) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunPreInitializedOnGLImportTest(true, VK_IMAGE_TILING_OPTIMAL, isSwiftshader(), enableDebugLayers()); } // Test that texture storage created from VkImage memory can be considered pre-initialized in GL, // using GL_ANGLE_memory_object_flags. Uses linear tiling. TEST_P(VulkanExternalImageTest, PreInitializedOnGLImportLinearWithFlags) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_memory_object_flags")); RunPreInitializedOnGLImportTest(true, VK_IMAGE_TILING_LINEAR, isSwiftshader(), enableDebugLayers()); } template void RunUninitializedOnGLImportTest(bool useMemoryObjectFlags, std::function useTexture, const uint32_t *expectInVulkan, bool isSwiftshader, bool enableDebugLayers) { ASSERT(EnsureGLExtensionEnabled(Traits::MemoryObjectExtension())); ASSERT(EnsureGLExtensionEnabled(Traits::SemaphoreExtension())); VkImageCreateFlags createFlags = kDefaultImageCreateFlags; VkImageUsageFlags usageFlags = kDefaultImageUsageFlags; AdjustCreateFlags(useMemoryObjectFlags, &createFlags); VulkanHelper helper; helper.initialize(isSwiftshader, enableDebugLayers); VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; ANGLE_SKIP_TEST_IF(!Traits::CanCreateImage(helper, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, createFlags, usageFlags)); ANGLE_SKIP_TEST_IF(!Traits::CanCreateSemaphore(helper)); VkSemaphore vkAcquireSemaphore = VK_NULL_HANDLE; VkResult result = Traits::CreateSemaphore(&helper, &vkAcquireSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkAcquireSemaphore != VK_NULL_HANDLE); VkSemaphore vkReleaseSemaphore = VK_NULL_HANDLE; result = Traits::CreateSemaphore(&helper, &vkReleaseSemaphore); EXPECT_EQ(result, VK_SUCCESS); EXPECT_TRUE(vkReleaseSemaphore != VK_NULL_HANDLE); typename Traits::Handle acquireSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkAcquireSemaphore, &acquireSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(acquireSemaphoreHandle, Traits::InvalidHandle()); typename Traits::Handle releaseSemaphoreHandle = Traits::InvalidHandle(); result = Traits::ExportSemaphore(&helper, vkReleaseSemaphore, &releaseSemaphoreHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(releaseSemaphoreHandle, Traits::InvalidHandle()); VkImage image = VK_NULL_HANDLE; VkDeviceMemory deviceMemory = VK_NULL_HANDLE; VkDeviceSize deviceMemorySize = 0; VkExtent3D extent = {kWidth, kHeight, 1}; result = Traits::CreateImage2D(&helper, format, createFlags, usageFlags, nullptr, extent, &image, &deviceMemory, &deviceMemorySize); EXPECT_EQ(result, VK_SUCCESS); typename Traits::Handle memoryHandle = Traits::InvalidHandle(); result = Traits::ExportMemory(&helper, deviceMemory, &memoryHandle); EXPECT_EQ(result, VK_SUCCESS); EXPECT_NE(memoryHandle, Traits::InvalidHandle()); { GLMemoryObject memoryObject; GLint dedicatedMemory = GL_TRUE; glMemoryObjectParameterivEXT(memoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicatedMemory); Traits::ImportMemory(memoryObject, deviceMemorySize, memoryHandle); GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (useMemoryObjectFlags) { glTexStorageMemFlags2DANGLE(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight, memoryObject, 0, createFlags, usageFlags, nullptr); } else { glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight, memoryObject, 0); } GLSemaphore glAcquireSemaphore; Traits::ImportSemaphore(glAcquireSemaphore, acquireSemaphoreHandle); // Submit the semaphore without touching the image helper.signalSemaphore(vkAcquireSemaphore); const GLuint barrierTexture = texture; const GLenum textureSrcLayout = GL_NONE; glWaitSemaphoreEXT(glAcquireSemaphore, 0, nullptr, 1, &barrierTexture, &textureSrcLayout); GLSemaphore glReleaseSemaphore; Traits::ImportSemaphore(glReleaseSemaphore, releaseSemaphoreHandle); const GLenum textureDstLayout = useTexture(texture); glSignalSemaphoreEXT(glReleaseSemaphore, 0, nullptr, 1, &barrierTexture, &textureDstLayout); const VkImageLayout imageLayout = GetPostReleaseVulkanLayout(textureDstLayout); helper.waitSemaphoreAndAcquireImage( image, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vkReleaseSemaphore); } EXPECT_GL_NO_ERROR(); // Verify the contents of the image from the Vulkan side too if needed if (expectInVulkan != nullptr) { uint8_t pixels[4]; const VkOffset3D offset = {}; const VkExtent3D readExtent = {1, 1, 1}; helper.readPixels(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_FORMAT_R8G8B8A8_UNORM, offset, readExtent, pixels, sizeof(pixels)); const uint32_t pixel = pixels[0] | pixels[1] << 8 | pixels[2] << 16 | pixels[3] << 24; EXPECT_EQ(pixel, *expectInVulkan); } vkDeviceWaitIdle(helper.getDevice()); vkDestroyImage(helper.getDevice(), image, nullptr); vkDestroySemaphore(helper.getDevice(), vkAcquireSemaphore, nullptr); vkDestroySemaphore(helper.getDevice(), vkReleaseSemaphore, nullptr); vkFreeMemory(helper.getDevice(), deviceMemory, nullptr); } // Test that texture storage created from VkImage memory can be imported as uninitialized in GL. TEST_P(VulkanExternalImageTest, UninitializedOnGLImport) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); constexpr uint32_t kExpect = 0xFF0000FF; auto render = [kExpect](GLuint texture) { GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); glClearColor(1, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); uint32_t pixel = 0u; glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); EXPECT_GL_NO_ERROR(); EXPECT_EQ(pixel, kExpect); EXPECT_GL_NO_ERROR(); return GL_LAYOUT_TRANSFER_SRC_EXT; }; RunUninitializedOnGLImportTest(false, render, &kExpect, isSwiftshader(), enableDebugLayers()); } // Test that texture storage created from VkImage memory can be imported as uninitialized in GL and // released without being touched. TEST_P(VulkanExternalImageTest, UninitializedOnGLImportAndExport) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); auto doNothing = [](GLuint) { return GL_NONE; }; RunUninitializedOnGLImportTest(false, doNothing, nullptr, isSwiftshader(), enableDebugLayers()); } // Test that texture storage created from VkImage memory can be imported as uninitialized in GL and // then used as the target of a copy. TEST_P(VulkanExternalImageTest, UninitializedOnGLImportAndCopy) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); constexpr uint32_t kExpect = 0xFF00FF00; auto copy = [kExpect](GLuint texture) { std::vector initData(kWidth * kHeight, GLColor::green); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, initData.data()); GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); uint32_t pixel = 0u; glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); EXPECT_GL_NO_ERROR(); EXPECT_EQ(pixel, kExpect); EXPECT_GL_NO_ERROR(); return GL_LAYOUT_TRANSFER_SRC_EXT; }; RunUninitializedOnGLImportTest(false, copy, &kExpect, isSwiftshader(), enableDebugLayers()); } // Test that texture storage created from VkImage memory can be imported as uninitialized in GL and // then used as sampler. Because the image is initialized, sampled results would be garbage, so // this test is primarily ensuring no validation errors are generated. TEST_P(VulkanExternalImageTest, UninitializedOnGLImportAndSample) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); auto sample = [this](GLuint texture) { GLProgram program; program.makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D()); drawQuad(program, std::string(essl1_shaders::PositionAttrib()), 0.0f); EXPECT_GL_NO_ERROR(); return GL_LAYOUT_SHADER_READ_ONLY_EXT; }; RunUninitializedOnGLImportTest(false, sample, nullptr, isSwiftshader(), enableDebugLayers()); } // Test that texture storage created from VkImage memory can be imported as uninitialized in GL and // then used as storage image. TEST_P(VulkanExternalImageTestES31, UninitializedOnGLImportAndStorageWrite) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd")); constexpr uint32_t kExpect = 0xFF00FFFF; auto storageWrite = [kExpect](GLuint texture) { constexpr char kCS[] = R"(#version 310 es layout(local_size_x=8, local_size_y=8) in; layout(rgba8) uniform highp writeonly image2D img; void main() { imageStore(img, ivec2(gl_GlobalInvocationID.xy), vec4(1, 1, 0, 1)); })"; GLProgram program; program.makeCompute(kCS); glUseProgram(program); glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); glDispatchCompute(kWidth / 8, kHeight / 8, 1); EXPECT_GL_NO_ERROR(); glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT); GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); uint32_t pixel = 0u; glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); EXPECT_GL_NO_ERROR(); EXPECT_EQ(pixel, kExpect); EXPECT_GL_NO_ERROR(); return GL_LAYOUT_TRANSFER_SRC_EXT; }; RunUninitializedOnGLImportTest(false, storageWrite, &kExpect, isSwiftshader(), enableDebugLayers()); } ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(VulkanExternalImageTest); ANGLE_INSTANTIATE_TEST_ES31(VulkanExternalImageTestES31); } // namespace angle