[vulkan] Implement external semaphore support for Fuchsia

This corresponds to the VK_FUCHSIA_external_semaphore extension
which uses a Zircon event object to communicate across processes.

Bug: b/140421726
Change-Id: I47b235d4ff7d787491738422bda6fdf853803ab7
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/35969
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Tested-by: David Turner <digit@google.com>
diff --git a/src/Vulkan/BUILD.gn b/src/Vulkan/BUILD.gn
index 067e32e..c42388d 100644
--- a/src/Vulkan/BUILD.gn
+++ b/src/Vulkan/BUILD.gn
@@ -92,6 +92,10 @@
     sources += [
       "VkSemaphoreExternalLinux.hpp",
     ]
+  } else if (is_fuchsia) {
+    sources += [
+      "VkSemaphoreExternalFuchsia.hpp",
+    ]
   } else {
     sources += [
       "VkSemaphoreExternalNone.hpp",
diff --git a/src/Vulkan/VkConfig.h b/src/Vulkan/VkConfig.h
index 9badfd8..ddb8d89 100644
--- a/src/Vulkan/VkConfig.h
+++ b/src/Vulkan/VkConfig.h
@@ -88,4 +88,8 @@
 #define SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD     1
 #endif
 
+#if VK_USE_PLATFORM_FUCHSIA
+#define SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT  1
+#endif
+
 #endif // VK_CONFIG_HPP_
diff --git a/src/Vulkan/VkGetProcAddress.cpp b/src/Vulkan/VkGetProcAddress.cpp
index eecfff8..4cc6a2d 100644
--- a/src/Vulkan/VkGetProcAddress.cpp
+++ b/src/Vulkan/VkGetProcAddress.cpp
@@ -346,6 +346,17 @@
 		}
 	},
 #endif
+
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+	// VK_FUCHSIA_external_semaphore
+	{
+		VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
+		{
+			MAKE_VULKAN_DEVICE_ENTRY(vkGetSemaphoreZirconHandleFUCHSIA),
+			MAKE_VULKAN_DEVICE_ENTRY(vkImportSemaphoreZirconHandleFUCHSIA),
+		}
+	},
+#endif
 };
 
 #undef MAKE_VULKAN_DEVICE_ENTRY
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 187c4bb..044ab15 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -381,6 +381,15 @@
 		return;
 	}
 #endif
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+	if (pExternalSemaphoreInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA)
+	{
+		pExternalSemaphoreProperties->compatibleHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA;
+		pExternalSemaphoreProperties->exportFromImportedHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA;
+		pExternalSemaphoreProperties->externalSemaphoreFeatures = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
+		return;
+	}
+#endif
 	pExternalSemaphoreProperties->compatibleHandleTypes = 0;
 	pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
 	pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
diff --git a/src/Vulkan/VkSemaphore.cpp b/src/Vulkan/VkSemaphore.cpp
index 62ff418..b495ebb 100644
--- a/src/Vulkan/VkSemaphore.cpp
+++ b/src/Vulkan/VkSemaphore.cpp
@@ -18,6 +18,8 @@
 
 #if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
 #include "VkSemaphoreExternalLinux.hpp"
+#elif SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+#include "VkSemaphoreExternalFuchsia.hpp"
 #else
 #include "VkSemaphoreExternalNone.hpp"
 #endif
@@ -226,4 +228,30 @@
 }
 #endif  // SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
 
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+VkResult Semaphore::importHandle(zx_handle_t handle, bool temporaryImport)
+{
+	std::unique_lock<std::mutex> lock(impl->mutex);
+	if (!impl->external)
+	{
+		impl->allocateExternalNoInit();
+	}
+	// NOTE: Imports are just moving a handle so cannot fail.
+	impl->external->importHandle(handle);
+	impl->temporaryImport = temporaryImport;
+	return VK_SUCCESS;
+}
+
+VkResult Semaphore::exportHandle(zx_handle_t *pHandle) const
+{
+	std::unique_lock<std::mutex> lock(impl->mutex);
+	if (!impl->external)
+	{
+		TRACE("Cannot export non-external semaphore");
+		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+	}
+	return impl->external->exportHandle(pHandle);
+}
+#endif  // SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+
 }  // namespace vk
diff --git a/src/Vulkan/VkSemaphore.hpp b/src/Vulkan/VkSemaphore.hpp
index d691c41..c0a8b04 100644
--- a/src/Vulkan/VkSemaphore.hpp
+++ b/src/Vulkan/VkSemaphore.hpp
@@ -18,6 +18,10 @@
 #include "VkConfig.h"
 #include "VkObject.hpp"
 
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+#include <zircon/types.h>
+#endif
+
 namespace vk
 {
 
@@ -44,6 +48,11 @@
 	VkResult exportFd(int* pFd) const;
 #endif
 
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+	VkResult importHandle(zx_handle_t handle, bool temporaryImport);
+	VkResult exportHandle(zx_handle_t *pHandle) const;
+#endif
+
 private:
 	class External;
 	class Impl;
diff --git a/src/Vulkan/VkSemaphoreExternalFuchsia.hpp b/src/Vulkan/VkSemaphoreExternalFuchsia.hpp
new file mode 100644
index 0000000..9e3e23b
--- /dev/null
+++ b/src/Vulkan/VkSemaphoreExternalFuchsia.hpp
@@ -0,0 +1,131 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef VK_SEMAPHORE_EXTERNAL_FUCHSIA_H_
+#define VK_SEMAPHORE_EXTERNAL_FUCHSIA_H_
+
+#include "VkDebug.hpp"
+
+#include <zircon/syscalls.h>
+
+// An external semaphore implementation for the Zircon kernel using a simple
+// Zircon event handle. This matches
+// VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA
+// which is not official yet but used by Fuchsia at the moment.
+
+namespace vk
+{
+
+class Semaphore::External {
+public:
+	// The type of external semaphore handle types supported by this implementation.
+	static const VkExternalSemaphoreHandleTypeFlags kExternalSemaphoreHandleType =
+			VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA;
+
+	// Default constructor. Note that one should call either init() or
+	// importFd() before any call to wait() or signal().
+	External() = default;
+
+	~External()
+	{
+		zx_handle_close(handle);
+	}
+
+	void init()
+	{
+		zx_status_t status = zx_event_create(0, &handle);
+		if (status != ZX_OK)
+		{
+			ABORT("zx_event_create() returned %d", status);
+		}
+	}
+
+	void importHandle(zx_handle_t new_handle)
+	{
+		zx_handle_close(handle);
+		handle = new_handle;
+	}
+
+	VkResult exportHandle(zx_handle_t* pHandle) const
+	{
+		zx_handle_t new_handle = ZX_HANDLE_INVALID;
+		zx_status_t status = zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &new_handle);
+		if (status != ZX_OK)
+		{
+			TRACE("zx_handle_duplicate() returned %d", status);
+			return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+		}
+		*pHandle = new_handle;
+		return VK_SUCCESS;
+	}
+
+	void wait()
+	{
+		zx_signals_t observed = 0;
+		zx_status_t status = zx_object_wait_one(
+				handle, ZX_EVENT_SIGNALED, ZX_TIME_INFINITE, &observed);
+		if (status != ZX_OK)
+		{
+			ABORT("zx_object_wait_one() returned %d", status);
+		}
+		if (observed != ZX_EVENT_SIGNALED)
+		{
+			ABORT("zx_object_wait_one() returned observed %x (%x expected)", observed, ZX_EVENT_SIGNALED);
+		}
+		// Need to unsignal the event now, as required by the Vulkan spec.
+		status = zx_object_signal(handle, ZX_EVENT_SIGNALED, 0);
+		if (status != ZX_OK)
+		{
+			ABORT("zx_object_signal() returned %d", status);
+		}
+	}
+
+	bool tryWait()
+	{
+		zx_signals_t observed = 0;
+		zx_status_t status = zx_object_wait_one(
+				handle, ZX_EVENT_SIGNALED, zx_clock_get_monotonic(), &observed);
+		if (status != ZX_OK)
+		{
+			ABORT("zx_object_wait_one() returned %d", status);
+		}
+		if (observed != ZX_EVENT_SIGNALED)
+		{
+			return false;
+		}
+		// Need to unsignal the event now, as required by the Vulkan spec.
+		status = zx_object_signal(handle, ZX_EVENT_SIGNALED, 0);
+		if (status != ZX_OK)
+		{
+			ABORT("zx_object_signal() returned %d", status);
+		}
+		return true;
+	}
+
+	void signal()
+	{
+		zx_status_t status = zx_object_signal(handle, 0, ZX_EVENT_SIGNALED);
+		if (status != ZX_OK)
+		{
+			ABORT("zx_object_signal() returned %d", status);
+		}
+	}
+
+private:
+	zx_handle_t handle = ZX_HANDLE_INVALID;
+};
+
+}  // namespace vk
+
+#endif  // VK_SEMAPHORE_EXTERNAL_FUCHSIA_H_
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index b7a9b1a..ebb2aed 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -233,6 +233,9 @@
 #if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
 	{ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION },
 #endif
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+	{ VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME, VK_FUCHSIA_EXTERNAL_SEMAPHORE_SPEC_VERSION },
+#endif
 	{ VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, VK_EXT_PROVOKING_VERTEX_SPEC_VERSION },
 };
 
@@ -1022,6 +1025,42 @@
 }
 #endif  // SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
 
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreZirconHandleFUCHSIA(
+	VkDevice                                        device,
+	const VkImportSemaphoreZirconHandleInfoFUCHSIA* pImportSemaphoreZirconHandleInfo)
+{
+	TRACE("(VkDevice device = %p, const VkImportSemaphoreZirconHandleInfoFUCHSIA* pImportSemaphoreZirconHandleInfo = %p)",
+	      device, pImportSemaphoreZirconHandleInfo);
+
+	if (pImportSemaphoreZirconHandleInfo->handleType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA)
+	{
+		UNIMPLEMENTED("pImportSemaphoreZirconHandleInfo->handleType");
+	}
+	bool temporaryImport = (pImportSemaphoreZirconHandleInfo->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT) != 0;
+
+	return vk::Cast(pImportSemaphoreZirconHandleInfo->semaphore)->importHandle(
+			pImportSemaphoreZirconHandleInfo->handle,
+			temporaryImport);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreZirconHandleFUCHSIA(
+	VkDevice                                     device,
+	const VkSemaphoreGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo,
+	zx_handle_t*                                 pZirconHandle)
+{
+	TRACE("(VkDevice device = %p, const VkSemaphoreGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo = %p, zx_handle_t* pZirconHandle = %p)",
+	      device, static_cast<const void*>(pGetZirconHandleInfo), static_cast<void*>(pZirconHandle));
+
+	if (pGetZirconHandleInfo->handleType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA)
+	{
+		UNIMPLEMENTED("pGetZirconHandleInfo->handleType");
+	}
+
+	return vk::Cast(pGetZirconHandleInfo->semaphore)->exportHandle(pZirconHandle);
+}
+#endif  // SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
+
 VKAPI_ATTR VkResult VKAPI_CALL vkCreateEvent(VkDevice device, const VkEventCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkEvent* pEvent)
 {
 	TRACE("(VkDevice device = %p, const VkEventCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkEvent* pEvent = %p)",