Vulkan: Implement glImportSemaphoreFdEXT
Allow importing file descriptors into semaphores on linux. This can be
used to synchronize ANGLE's GL renderer with respect to vulkan
composition in chromium.
Bug: angleproject:3289
Change-Id: I04ba3bbb2e343baa000ff89c21c03ca36163a713
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1623812
Commit-Queue: Michael Spang <spang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 70654d7..88af5a2 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -7265,7 +7265,9 @@
void Context::importSemaphoreFd(GLuint semaphore, HandleType handleType, GLint fd)
{
- UNIMPLEMENTED();
+ Semaphore *semaphoreObject = getSemaphore(semaphore);
+ ASSERT(semaphoreObject != nullptr);
+ ANGLE_CONTEXT_TRY(semaphoreObject->importFd(this, handleType, fd));
}
void Context::eGLImageTargetTexture2D(TextureType target, GLeglImageOES image)
diff --git a/src/libANGLE/Semaphore.cpp b/src/libANGLE/Semaphore.cpp
index 10bcf02..a224bec 100644
--- a/src/libANGLE/Semaphore.cpp
+++ b/src/libANGLE/Semaphore.cpp
@@ -25,4 +25,9 @@
mImplementation->onDestroy(context);
}
+angle::Result Semaphore::importFd(Context *context, HandleType handleType, GLint fd)
+{
+ return mImplementation->importFd(context, handleType, fd);
+}
+
} // namespace gl
diff --git a/src/libANGLE/Semaphore.h b/src/libANGLE/Semaphore.h
index 2e65d2e..db2d25a 100644
--- a/src/libANGLE/Semaphore.h
+++ b/src/libANGLE/Semaphore.h
@@ -11,7 +11,9 @@
#include <memory>
#include "angle_gl.h"
+#include "common/PackedEnums.h"
#include "common/angleutils.h"
+#include "libANGLE/Error.h"
#include "libANGLE/RefCountObject.h"
namespace rx
@@ -34,6 +36,8 @@
rx::SemaphoreImpl *getImplementation() const { return mImplementation.get(); }
+ angle::Result importFd(Context *context, HandleType handleType, GLint fd);
+
private:
std::unique_ptr<rx::SemaphoreImpl> mImplementation;
};
diff --git a/src/libANGLE/renderer/SemaphoreImpl.h b/src/libANGLE/renderer/SemaphoreImpl.h
index 813524c..2ebff59 100644
--- a/src/libANGLE/renderer/SemaphoreImpl.h
+++ b/src/libANGLE/renderer/SemaphoreImpl.h
@@ -28,6 +28,8 @@
virtual ~SemaphoreImpl() {}
virtual void onDestroy(const gl::Context *context) = 0;
+
+ virtual angle::Result importFd(gl::Context *context, gl::HandleType handleType, GLint fd) = 0;
};
} // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index d8388a3..0b84b8d 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -1012,6 +1012,7 @@
if (getFeatures().supportsExternalSemaphoreFd.enabled)
{
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
+ InitExternalSemaphoreFdFunctions(mInstance);
}
if (getFeatures().supportsExternalSemaphoreFd.enabled)
diff --git a/src/libANGLE/renderer/vulkan/SemaphoreVk.cpp b/src/libANGLE/renderer/vulkan/SemaphoreVk.cpp
index 16dacc3..90bdf6b 100644
--- a/src/libANGLE/renderer/vulkan/SemaphoreVk.cpp
+++ b/src/libANGLE/renderer/vulkan/SemaphoreVk.cpp
@@ -9,6 +9,8 @@
#include "common/debug.h"
#include "libANGLE/Context.h"
+#include "libANGLE/renderer/vulkan/ContextVk.h"
+#include "libANGLE/renderer/vulkan/RendererVk.h"
namespace rx
{
@@ -17,6 +19,50 @@
SemaphoreVk::~SemaphoreVk() = default;
-void SemaphoreVk::onDestroy(const gl::Context *context) {}
+void SemaphoreVk::onDestroy(const gl::Context *context)
+{
+ ContextVk *contextVk = vk::GetImpl(context);
+ RendererVk *renderer = contextVk->getRenderer();
+
+ Serial currentSerial = renderer->getCurrentQueueSerial();
+ renderer->releaseObject(currentSerial, &mSemaphore);
+}
+
+angle::Result SemaphoreVk::importFd(gl::Context *context, gl::HandleType handleType, GLint fd)
+{
+ switch (handleType)
+ {
+ case gl::HandleType::OpaqueFd:
+ return importOpaqueFd(context, fd);
+
+ default:
+ ANGLE_VK_UNREACHABLE(vk::GetImpl(context));
+ return angle::Result::Stop;
+ }
+}
+
+angle::Result SemaphoreVk::importOpaqueFd(gl::Context *context, GLint fd)
+{
+ ContextVk *contextVk = vk::GetImpl(context);
+ RendererVk *renderer = contextVk->getRenderer();
+
+ if (!mSemaphore.valid())
+ {
+ mSemaphore.init(renderer->getDevice());
+ }
+
+ ASSERT(mSemaphore.valid());
+
+ VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo = {};
+ importSemaphoreFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+ importSemaphoreFdInfo.semaphore = mSemaphore.getHandle();
+ importSemaphoreFdInfo.flags = 0;
+ importSemaphoreFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
+ importSemaphoreFdInfo.fd = fd;
+
+ ANGLE_VK_TRY(contextVk, vkImportSemaphoreFdKHR(renderer->getDevice(), &importSemaphoreFdInfo));
+
+ return angle::Result::Continue;
+}
} // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/SemaphoreVk.h b/src/libANGLE/renderer/vulkan/SemaphoreVk.h
index edbf6f9..a29b792 100644
--- a/src/libANGLE/renderer/vulkan/SemaphoreVk.h
+++ b/src/libANGLE/renderer/vulkan/SemaphoreVk.h
@@ -22,6 +22,13 @@
~SemaphoreVk() override;
void onDestroy(const gl::Context *context) override;
+
+ angle::Result importFd(gl::Context *context, gl::HandleType handleType, GLint fd) override;
+
+ private:
+ angle::Result importOpaqueFd(gl::Context *context, GLint fd);
+
+ vk::Semaphore mSemaphore;
};
} // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/vk_utils.cpp b/src/libANGLE/renderer/vulkan/vk_utils.cpp
index 01d88d9..e266e3e 100644
--- a/src/libANGLE/renderer/vulkan/vk_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_utils.cpp
@@ -522,6 +522,9 @@
// VK_KHR_get_physical_device_properties2
PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR = nullptr;
+// VK_KHR_external_semaphore_fd
+PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr;
+
#if defined(ANGLE_PLATFORM_FUCHSIA)
// VK_FUCHSIA_imagepipe_surface
PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA = nullptr;
@@ -572,6 +575,11 @@
}
#endif
+void InitExternalSemaphoreFdFunctions(VkInstance instance)
+{
+ GET_FUNC(vkImportSemaphoreFdKHR);
+}
+
#undef GET_FUNC
namespace gl_vk
diff --git a/src/libANGLE/renderer/vulkan/vk_utils.h b/src/libANGLE/renderer/vulkan/vk_utils.h
index f3fae2e..707290f 100644
--- a/src/libANGLE/renderer/vulkan/vk_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_utils.h
@@ -494,6 +494,9 @@
// VK_KHR_get_physical_device_properties2
extern PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR;
+// VK_KHR_external_semaphore_fd
+extern PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR;
+
// Lazily load entry points for each extension as necessary.
void InitDebugUtilsEXTFunctions(VkInstance instance);
void InitDebugReportEXTFunctions(VkInstance instance);
@@ -512,6 +515,8 @@
void InitExternalMemoryHardwareBufferANDROIDFunctions(VkInstance instance);
#endif
+void InitExternalSemaphoreFdFunctions(VkInstance instance);
+
namespace gl_vk
{
VkRect2D GetRect(const gl::Rectangle &source);
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index da61ed44..b98de66 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -3374,8 +3374,16 @@
return false;
}
- UNIMPLEMENTED();
- return false;
+ switch (handleType)
+ {
+ case HandleType::OpaqueFd:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidHandleType);
+ return false;
+ }
+
+ return true;
}
bool ValidateMapBufferBase(Context *context, BufferBinding target)
diff --git a/src/tests/gl_tests/SemaphoreTest.cpp b/src/tests/gl_tests/SemaphoreTest.cpp
index 564e391..0ad11d0 100644
--- a/src/tests/gl_tests/SemaphoreTest.cpp
+++ b/src/tests/gl_tests/SemaphoreTest.cpp
@@ -48,6 +48,21 @@
EXPECT_GL_NO_ERROR();
}
+// glImportSemaphoreFdEXT must fail for handle types that are not file descriptors.
+TEST_P(SemaphoreTest, ShouldFailValidationOnImportFdUnsupportedHandleType)
+{
+ ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd"));
+
+ {
+ GLSemaphore semaphore;
+ int fd = -1;
+ glImportSemaphoreFdEXT(semaphore, GL_HANDLE_TYPE_OPAQUE_WIN32_EXT, fd);
+ EXPECT_GL_ERROR(GL_INVALID_ENUM);
+ }
+
+ EXPECT_GL_NO_ERROR();
+}
+
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(SemaphoreTest,
diff --git a/src/tests/gl_tests/VulkanExternalImageTest.cpp b/src/tests/gl_tests/VulkanExternalImageTest.cpp
index 1f59880..96e093b 100644
--- a/src/tests/gl_tests/VulkanExternalImageTest.cpp
+++ b/src/tests/gl_tests/VulkanExternalImageTest.cpp
@@ -55,7 +55,7 @@
};
// glImportMemoryFdEXT must be able to import a valid opaque fd.
-TEST_P(VulkanExternalImageTest, ShouldImportOpaqueFd)
+TEST_P(VulkanExternalImageTest, ShouldImportMemoryOpaqueFd)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_memory_object_fd"));
@@ -90,6 +90,35 @@
vkFreeMemory(helper.getDevice(), deviceMemory, nullptr);
}
+// glImportSemaphoreFdEXT must be able to import a valid opaque fd.
+TEST_P(VulkanExternalImageTest, ShouldImportSemaphoreOpaqueFd)
+{
+ ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_semaphore_fd"));
+
+ VulkanExternalHelper helper;
+ helper.initialize();
+
+ ANGLE_SKIP_TEST_IF(!helper.canCreateSemaphoreOpaqueFd());
+
+ VkSemaphore vkSemaphore = VK_NULL_HANDLE;
+ VkResult result = helper.createSemaphoreOpaqueFd(&vkSemaphore);
+ EXPECT_EQ(result, VK_SUCCESS);
+
+ int fd = kInvalidFd;
+ result = helper.exportSemaphoreOpaqueFd(vkSemaphore, &fd);
+ EXPECT_EQ(result, VK_SUCCESS);
+ EXPECT_NE(fd, kInvalidFd);
+
+ {
+ GLSemaphore glSemaphore;
+ glImportSemaphoreFdEXT(glSemaphore, GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd);
+ }
+
+ EXPECT_GL_NO_ERROR();
+
+ vkDestroySemaphore(helper.getDevice(), vkSemaphore, nullptr);
+}
+
// Test creating and clearing a simple RGBA8 texture in a opaque fd.
TEST_P(VulkanExternalImageTest, ShouldClearOpaqueFdRGBA8)
{
diff --git a/src/tests/test_utils/VulkanExternalHelper.cpp b/src/tests/test_utils/VulkanExternalHelper.cpp
index f43b7cd..727ea5c 100644
--- a/src/tests/test_utils/VulkanExternalHelper.cpp
+++ b/src/tests/test_utils/VulkanExternalHelper.cpp
@@ -277,6 +277,11 @@
vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceImageFormatProperties2"));
vkGetMemoryFdKHR = reinterpret_cast<PFN_vkGetMemoryFdKHR>(
vkGetInstanceProcAddr(mInstance, "vkGetMemoryFdKHR"));
+ vkGetSemaphoreFdKHR = reinterpret_cast<PFN_vkGetSemaphoreFdKHR>(
+ vkGetInstanceProcAddr(mInstance, "vkGetSemaphoreFdKHR"));
+ vkGetPhysicalDeviceExternalSemaphorePropertiesKHR =
+ reinterpret_cast<PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR>(
+ vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR"));
}
bool VulkanExternalHelper::canCreateImageOpaqueFd(VkFormat format,
@@ -331,8 +336,8 @@
{
return false;
}
- if (!(externalImageFormatProperties.externalMemoryProperties.externalMemoryFeatures &
- kRequiredFeatures))
+ if ((externalImageFormatProperties.externalMemoryProperties.externalMemoryFeatures &
+ kRequiredFeatures) != kRequiredFeatures)
{
return false;
}
@@ -434,4 +439,62 @@
return vkGetMemoryFdKHR(mDevice, &memoryGetFdInfo, fd);
}
+bool VulkanExternalHelper::canCreateSemaphoreOpaqueFd() const
+{
+ if (!mHasExternalSemaphoreFd || !vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)
+ {
+ return false;
+ }
+
+ VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = {
+ /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
+ /* .pNext = */ nullptr,
+ /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
+ };
+
+ VkExternalSemaphoreProperties externalSemaphoreProperties = {};
+ vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo,
+ &externalSemaphoreProperties);
+
+ constexpr VkExternalSemaphoreFeatureFlags kRequiredFeatures =
+ VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
+
+ if ((externalSemaphoreProperties.externalSemaphoreFeatures & kRequiredFeatures) !=
+ kRequiredFeatures)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+VkResult VulkanExternalHelper::createSemaphoreOpaqueFd(VkSemaphore *semaphore)
+{
+ VkExportSemaphoreCreateInfo exportSemaphoreCreateInfo = {
+ /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
+ /* .pNext = */ nullptr,
+ /* .handleTypes = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
+ };
+
+ VkSemaphoreCreateInfo semaphoreCreateInfo = {
+ /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ /* .pNext = */ &exportSemaphoreCreateInfo,
+ /* .flags = */ 0,
+ };
+
+ return vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, semaphore);
+}
+
+VkResult VulkanExternalHelper::exportSemaphoreOpaqueFd(VkSemaphore semaphore, int *fd)
+{
+ VkSemaphoreGetFdInfoKHR semaphoreGetFdInfo = {
+ /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
+ /* .pNext = */ nullptr,
+ /* .semaphore = */ semaphore,
+ /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
+ };
+
+ return vkGetSemaphoreFdKHR(mDevice, &semaphoreGetFdInfo, fd);
+}
+
} // namespace angle
diff --git a/src/tests/test_utils/VulkanExternalHelper.h b/src/tests/test_utils/VulkanExternalHelper.h
index 519a9f0..e1939e8 100644
--- a/src/tests/test_utils/VulkanExternalHelper.h
+++ b/src/tests/test_utils/VulkanExternalHelper.h
@@ -35,6 +35,11 @@
VkDeviceSize *deviceMemorySizeOut);
VkResult exportMemoryOpaqueFd(VkDeviceMemory deviceMemory, int *fd);
+ // VK_KHR_external_semaphore_fd
+ bool canCreateSemaphoreOpaqueFd() const;
+ VkResult createSemaphoreOpaqueFd(VkSemaphore *semaphore);
+ VkResult exportSemaphoreOpaqueFd(VkSemaphore semaphore, int *fd);
+
private:
VkInstance mInstance = VK_NULL_HANDLE;
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
@@ -50,6 +55,9 @@
PFN_vkGetPhysicalDeviceImageFormatProperties2 vkGetPhysicalDeviceImageFormatProperties2 =
nullptr;
PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR = nullptr;
+ PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr;
+ PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR
+ vkGetPhysicalDeviceExternalSemaphorePropertiesKHR = nullptr;
};
} // namespace angle
diff --git a/src/tests/test_utils/gl_raii.h b/src/tests/test_utils/gl_raii.h
index fd9ed02..4a2c9c1 100644
--- a/src/tests/test_utils/gl_raii.h
+++ b/src/tests/test_utils/gl_raii.h
@@ -101,6 +101,11 @@
public:
GLSampler() : GLWrapper(&glGenSamplers, &glDeleteSamplers) {}
};
+class GLSemaphore : public GLWrapper
+{
+ public:
+ GLSemaphore() : GLWrapper(&glGenSemaphoresEXT, &glDeleteSemaphoresEXT) {}
+};
class GLTransformFeedback : public GLWrapper
{
public: