[vulkan] Implement Linux-based external semaphore
Add external semaphore support for Linux and Android
based on a process-shared pthread mutex + condition
variable stored in a shared memory region backed by
memfd_create().
This takes care of waiting for external semaphores
in a background thread when invoked from a Yarn fiber.
Test: dEQP-VK.api.external.semaphore.opaque_fd*
Bug: b/140421726
Change-Id: Ifa74c807d3e33914e5a37dd96650c312246c8e4f
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/35939
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Tested-by: David Turner <digit@google.com>
Kokoro-Presubmit: David Turner <digit@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Vulkan/BUILD.gn b/src/Vulkan/BUILD.gn
index 8e19658..308b718 100644
--- a/src/Vulkan/BUILD.gn
+++ b/src/Vulkan/BUILD.gn
@@ -83,6 +83,15 @@
"VkShaderModule.hpp",
"VulkanPlatform.h",
]
+ if (is_linux || is_android) {
+ sources += [
+ "VkSemaphoreExternalLinux.hpp",
+ ]
+ } else {
+ sources += [
+ "VkSemaphoreExternalNone.hpp",
+ ]
+ }
}
swiftshader_shared_library("swiftshader_libvulkan") {
diff --git a/src/Vulkan/VkConfig.h b/src/Vulkan/VkConfig.h
index 3bc9819..9badfd8 100644
--- a/src/Vulkan/VkConfig.h
+++ b/src/Vulkan/VkConfig.h
@@ -84,4 +84,8 @@
}
+#if VK_USE_PLATFORM_XLIB_KHR || VK_USE_PLATFORM_ANDROID_KHR
+#define SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD 1
+#endif
+
#endif // VK_CONFIG_HPP_
diff --git a/src/Vulkan/VkGetProcAddress.cpp b/src/Vulkan/VkGetProcAddress.cpp
index 9e5d8f4..28bdcaa 100644
--- a/src/Vulkan/VkGetProcAddress.cpp
+++ b/src/Vulkan/VkGetProcAddress.cpp
@@ -334,6 +334,17 @@
}
},
#endif
+
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
+ // VK_KHR_external_semaphore_fd
+ {
+ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
+ {
+ MAKE_VULKAN_DEVICE_ENTRY(vkGetSemaphoreFdKHR),
+ MAKE_VULKAN_DEVICE_ENTRY(vkImportSemaphoreFdKHR),
+ }
+ },
+#endif
};
#undef MAKE_VULKAN_DEVICE_ENTRY
@@ -443,4 +454,4 @@
}
};
-#endif
\ No newline at end of file
+#endif
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 44a2ba4..5ccba6f 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -357,6 +357,15 @@
void PhysicalDevice::getProperties(const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) const
{
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
+ if (pExternalSemaphoreInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT)
+ {
+ pExternalSemaphoreProperties->compatibleHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
+ pExternalSemaphoreProperties->exportFromImportedHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
+ 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 04a1c26..62ff418 100644
--- a/src/Vulkan/VkSemaphore.cpp
+++ b/src/Vulkan/VkSemaphore.cpp
@@ -14,8 +14,21 @@
#include "VkSemaphore.hpp"
+#include "VkConfig.h"
+
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
+#include "VkSemaphoreExternalLinux.hpp"
+#else
+#include "VkSemaphoreExternalNone.hpp"
+#endif
+
+#include "marl/blockingcall.h"
#include "marl/conditionvariable.h"
+
+#include <functional>
+#include <memory>
#include <mutex>
+#include <utility>
namespace vk
{
@@ -24,17 +37,115 @@
class Semaphore::Impl
{
public:
- Impl() = default;
+ // Create a new instance. The external instance will be allocated only
+ // the pCreateInfo->pNext chain indicates it needs to be exported.
+ Impl(const VkSemaphoreCreateInfo* pCreateInfo) {
+ bool exportSemaphore = false;
+ for (const auto* nextInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
+ nextInfo != nullptr; nextInfo = nextInfo->pNext)
+ {
+ if (nextInfo->sType == VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO)
+ {
+ const auto* exportInfo = reinterpret_cast<const VkExportSemaphoreCreateInfo *>(nextInfo);
+ if (exportInfo->handleTypes != External::kExternalSemaphoreHandleType)
+ {
+ UNIMPLEMENTED("exportInfo->handleTypes");
+ }
+ exportSemaphore = true;
+ break;
+ }
+ }
+
+ if (exportSemaphore)
+ {
+ allocateExternalNoInit();
+ external->init();
+ }
+ }
+
+ ~Impl() {
+ deallocateExternal();
+ }
+
+ // Deallocate the External semaphore if any.
+ void deallocateExternal()
+ {
+ if (external)
+ {
+ external->~External();
+ external = nullptr;
+ }
+ }
+
+ // Allocate the external semaphore.
+ // Note that this does not allocate the internal resource, which must be
+ // performed by calling external->init(), or importing one using
+ // a platform-specific external->importXXX(...) method.
+ void allocateExternalNoInit()
+ {
+ external = new (externalStorage) External();
+ }
void wait()
{
+ if (external)
+ {
+ if (!external->tryWait())
+ {
+ // Dispatch the external wait to a background thread.
+ // Even if this creates a new thread on each
+ // call, it is assumed that this is negligible
+ // compared with the actual semaphore wait()
+ // operation.
+ marl::blocking_call([this](){
+ external->wait();
+ });
+ }
+
+ // If the import was temporary, reset the semaphore to its
+ // permanent state by getting rid of |external|.
+ // See "6.4.5. Importing Semaphore Payloads" in Vulkan 1.1 spec.
+ if (temporaryImport)
+ {
+ deallocateExternal();
+ temporaryImport = false;
+ }
+ }
+ else
+ {
+ waitInternal();
+ }
+ }
+
+ void signal()
+ {
+ if (external)
+ {
+ // Assumes that signalling an external semaphore is non-blocking,
+ // so it can be performed directly either from a fiber or thread.
+ external->signal();
+ }
+ else
+ {
+ signalInternal();
+ }
+ }
+
+private:
+ // Necessary to make ::importXXX() and ::exportXXX() simpler.
+ friend Semaphore;
+
+ void waitInternal()
+ {
+ // Wait on the marl condition variable only.
std::unique_lock<std::mutex> lock(mutex);
condition.wait(lock, [this]{ return this->signaled; });
signaled = false; // Vulkan requires resetting after waiting.
}
- void signal()
+ void signalInternal()
{
+ // Signal the marl condition variable only.
std::unique_lock<std::mutex> lock(mutex);
if (!signaled)
{
@@ -43,15 +154,23 @@
}
}
-private:
+ // Implementation of a non-external semaphore based on Marl.
std::mutex mutex;
marl::ConditionVariable condition;
bool signaled = false;
+
+ // Optional external semaphore data might be referenced and stored here.
+ External* external = nullptr;
+
+ // Set to true if |external| comes from a temporary import.
+ bool temporaryImport = false;
+
+ alignas(External) char externalStorage[sizeof(External)];
};
Semaphore::Semaphore(const VkSemaphoreCreateInfo* pCreateInfo, void* mem)
{
- impl = new (mem) Impl();
+ impl = new (mem) Impl(pCreateInfo);
}
void Semaphore::destroy(const VkAllocationCallbacks* pAllocator)
@@ -75,4 +194,36 @@
impl->signal();
}
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
+VkResult Semaphore::importFd(int fd, bool temporaryImport)
+{
+ std::unique_lock<std::mutex> lock(impl->mutex);
+ if (!impl->external)
+ {
+ impl->allocateExternalNoInit();
+ }
+ VkResult result = impl->external->importFd(fd);
+ if (result != VK_SUCCESS)
+ {
+ impl->deallocateExternal();
+ }
+ else
+ {
+ impl->temporaryImport = temporaryImport;
+ }
+ return result;
+}
+
+VkResult Semaphore::exportFd(int* pFd) 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->exportFd(pFd);
+}
+#endif // SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
+
} // namespace vk
diff --git a/src/Vulkan/VkSemaphore.hpp b/src/Vulkan/VkSemaphore.hpp
index 1f9b160..d691c41 100644
--- a/src/Vulkan/VkSemaphore.hpp
+++ b/src/Vulkan/VkSemaphore.hpp
@@ -15,6 +15,7 @@
#ifndef VK_SEMAPHORE_HPP_
#define VK_SEMAPHORE_HPP_
+#include "VkConfig.h"
#include "VkObject.hpp"
namespace vk
@@ -38,7 +39,13 @@
void signal();
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
+ VkResult importFd(int fd, bool temporaryImport);
+ VkResult exportFd(int* pFd) const;
+#endif
+
private:
+ class External;
class Impl;
Impl* impl = nullptr;
};
diff --git a/src/Vulkan/VkSemaphoreExternalLinux.hpp b/src/Vulkan/VkSemaphoreExternalLinux.hpp
new file mode 100644
index 0000000..05983cf
--- /dev/null
+++ b/src/Vulkan/VkSemaphoreExternalLinux.hpp
@@ -0,0 +1,246 @@
+// 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_LINUX_H_
+#define VK_SEMAPHORE_EXTERNAL_LINUX_H_
+
+#include "System/Linux/MemFd.hpp"
+#include "System/Memory.hpp"
+
+#include <errno.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/mman.h>
+
+// An external semaphore implementation for Linux, that uses memfd-backed
+// shared memory regions as the underlying implementation. The region contains
+// a single SharedSemaphore instance, which is a reference-counted semaphore
+// implementation based on a pthread process-shared mutex + condition variable
+// pair.
+//
+// This implementation works on any Linux system with at least kernel 3.17
+// (which should be sufficient for any not-so-recent Android system) and doesn't
+// require special libraries installed on the system.
+//
+// NOTE: This is not interoperable with other Linux ICDs that use Linux kernel
+// sync file objects (which correspond to
+// VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) instead.
+//
+
+namespace linux
+{
+
+// A process-shared semaphore implementation that can be stored in
+// a process-shared memory region. It also includes a reference count to
+// ensure it is only destroyed when the last reference to it is dropped.
+class SharedSemaphore
+{
+public:
+ SharedSemaphore()
+ {
+ pthread_mutexattr_t mattr;
+ pthread_mutexattr_init(&mattr);
+ pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&mutex, &mattr);
+ pthread_mutexattr_destroy(&mattr);
+
+ pthread_condattr_t cattr;
+ pthread_condattr_init(&cattr);
+ pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+ pthread_cond_init(&cond, &cattr);
+ pthread_condattr_destroy(&cattr);
+ }
+
+ ~SharedSemaphore()
+ {
+ pthread_cond_destroy(&cond);
+ pthread_mutex_destroy(&mutex);
+ }
+
+ // Increment reference count.
+ void addRef()
+ {
+ pthread_mutex_lock(&mutex);
+ ref_count++;
+ pthread_mutex_unlock(&mutex);
+ }
+
+ // Decrement reference count and returns true iff it reaches 0.
+ bool deref()
+ {
+ pthread_mutex_lock(&mutex);
+ bool result = (--ref_count == 0);
+ pthread_mutex_unlock(&mutex);
+ return result;
+ }
+
+ void wait()
+ {
+ pthread_mutex_lock(&mutex);
+ while (!signaled)
+ {
+ pthread_cond_wait(&cond, &mutex);
+ }
+ // From Vulkan 1.1.119 spec, section 6.4.2:
+ // Unlike fences or events, the act of waiting for a semaphore also
+ // unsignals that semaphore.
+ signaled = false;
+ pthread_mutex_unlock(&mutex);
+ }
+
+ // Just like wait() but never blocks. Returns true if the semaphore
+ // was signaled (and reset by the function), or false otherwise.
+ // Used to avoid using a background thread for waiting in the case
+ // where the semaphore is already signaled.
+ bool tryWait()
+ {
+ pthread_mutex_lock(&mutex);
+ bool result = signaled;
+ if (result)
+ {
+ signaled = false;
+ }
+ pthread_mutex_unlock(&mutex);
+ return result;
+ }
+
+ void signal()
+ {
+ pthread_mutex_lock(&mutex);
+ signaled = true;
+ pthread_cond_broadcast(&cond);
+ pthread_mutex_unlock(&mutex);
+ }
+
+private:
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int ref_count = 1;
+ bool signaled = false;
+};
+
+} // namespace linux
+
+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_OPAQUE_FD_BIT;
+
+ // Default constructor. Note that one should call either init() or
+ // importFd() before any call to wait() or signal().
+ External() = default;
+
+ ~External() { close(); }
+
+ // Initialize instance by creating a new shared memory region.
+ void init()
+ {
+ // Allocate or import the region's file descriptor.
+ const size_t size = sw::memoryPageSize();
+ // To be exportable, the PosixSemaphore must be stored in a shared
+ // memory region.
+ static int counter = 0;
+ char name[40];
+ snprintf(name, sizeof(name), "SwiftShader.Semaphore.%d", ++counter);
+ if (!memfd.allocate(name, size))
+ {
+ ABORT("memfd.allocate() returned %s", strerror(errno));
+ }
+ mapRegion(size, true);
+ }
+
+ // Import an existing semaphore through its file descriptor.
+ VkResult importFd(int fd)
+ {
+ close();
+ memfd.importFd(fd);
+ mapRegion(sw::memoryPageSize(), false);
+ return VK_SUCCESS;
+ }
+
+ // Export the current semaphore as a duplicated file descriptor to the same
+ // region. This can be consumed by importFd() running in a different
+ // process.
+ VkResult exportFd(int* pFd) const
+ {
+ int fd = memfd.exportFd();
+ if (fd < 0)
+ {
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+ *pFd = fd;
+ return VK_SUCCESS;
+ }
+
+ void wait()
+ {
+ semaphore->wait();
+ }
+
+ bool tryWait()
+ {
+ return semaphore->tryWait();
+ }
+
+ void signal()
+ {
+ semaphore->signal();
+ }
+
+private:
+ // Unmap the semaphore if needed and close its file descriptor.
+ void close()
+ {
+ if (semaphore)
+ {
+ if (semaphore->deref())
+ {
+ semaphore->~SharedSemaphore();
+ }
+ memfd.unmap(semaphore, sw::memoryPageSize());
+ memfd.close();
+ semaphore = nullptr;
+ }
+ }
+
+ // Remap the shared region and setup the semaphore or increment its reference count.
+ void mapRegion(size_t size, bool needInitialization)
+ {
+ // Map the region into memory and point the semaphore to it.
+ void* addr = memfd.mapReadWrite(0, size);
+ if (!addr)
+ {
+ ABORT("mmap() failed: %s", strerror(errno));
+ }
+ semaphore = reinterpret_cast<linux::SharedSemaphore *>(addr);
+ if (needInitialization)
+ {
+ new (semaphore) linux::SharedSemaphore();
+ }
+ else
+ {
+ semaphore->addRef();
+ }
+ }
+
+ linux::MemFd memfd;
+ linux::SharedSemaphore* semaphore = nullptr;
+};
+
+} // namespace vk
+
+#endif // VK_SEMAPHORE_EXTERNAL_LINUX_H_
diff --git a/src/Vulkan/VkSemaphoreExternalNone.hpp b/src/Vulkan/VkSemaphoreExternalNone.hpp
new file mode 100644
index 0000000..d056e6c
--- /dev/null
+++ b/src/Vulkan/VkSemaphoreExternalNone.hpp
@@ -0,0 +1,41 @@
+// 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_NONE_H_
+#define VK_SEMAPHORE_EXTERNAL_NONE_H_
+
+namespace vk
+{
+
+// Empty external sempahore implementation.
+class Semaphore::External {
+public:
+ // The type of external semaphore handle types supported by this implementation.
+ static const VkExternalSemaphoreHandleTypeFlags kExternalSemaphoreHandleType = 0;
+
+ void init() {}
+
+ void wait() {}
+
+ bool tryWait() { return true; }
+
+ void signal() {}
+
+private:
+ int dummy;
+};
+
+} // namespace vk
+
+#endif // VK_SEMAPHORE_EXTERNAL_NONE_H_
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 11bac00..320ec99 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -228,6 +228,9 @@
// (from KHR_swapchain v70) to vkBindImageMemory2.
{ VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME, 7 },
#endif
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
+ { VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION },
+#endif
};
VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance)
@@ -945,9 +948,9 @@
TRACE("(VkDevice device = %p, const VkSemaphoreCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkSemaphore* pSemaphore = %p)",
device, pCreateInfo, pAllocator, pSemaphore);
- if(pCreateInfo->pNext || pCreateInfo->flags)
+ if(pCreateInfo->flags)
{
- UNIMPLEMENTED("pCreateInfo->pNext || pCreateInfo->flags");
+ UNIMPLEMENTED("pCreateInfo->flags");
}
return vk::Semaphore::Create(pAllocator, pCreateInfo, pSemaphore);
@@ -961,6 +964,35 @@
vk::destroy(semaphore, pAllocator);
}
+#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreFdKHR(VkDevice device, const VkSemaphoreGetFdInfoKHR* pGetFdInfo, int* pFd)
+{
+ TRACE("(VkDevice device = %p, const VkSemaphoreGetFdInfoKHR* pGetFdInfo = %p, int* pFd = %p)",
+ device, static_cast<const void*>(pGetFdInfo), static_cast<void*>(pFd));
+
+ if (pGetFdInfo->handleType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT)
+ {
+ UNIMPLEMENTED("pGetFdInfo->handleType");
+ }
+
+ return vk::Cast(pGetFdInfo->semaphore)->exportFd(pFd);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreFdKHR(VkDevice device, const VkImportSemaphoreFdInfoKHR* pImportSemaphoreInfo)
+{
+ TRACE("(VkDevice device = %p, const VkImportSemaphoreFdInfoKHR* pImportSemaphoreInfo = %p",
+ device, static_cast<const void*>(pImportSemaphoreInfo));
+
+ if (pImportSemaphoreInfo->handleType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT)
+ {
+ UNIMPLEMENTED("pImportSemaphoreInfo->handleType");
+ }
+ bool temporaryImport = (pImportSemaphoreInfo->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT) != 0;
+
+ return vk::Cast(pImportSemaphoreInfo->semaphore)->importFd(pImportSemaphoreInfo->fd, temporaryImport);
+}
+#endif // SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
+
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)",