Introduce KHR_external_semaphore_fd only for FD==-1

There is a special use case for when importing/exporting a semaphore of
handle type `VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT` and FD == -1.
According to the Vulkan specs, calling `vkImportSemaphoreFdKHR` has the
effect of signaling the binary semaphore and calling `vkGetSemaphoreFdKHR`
has the effect of waiting on the binary semaphore.

Outside of KHR_external_semaphore_fd extension, there is no other way to
achieve this behaviour. For example, `vkWaitSemaphores` does not operate
on a binary semaphore and there is no other defined way for a CPU to send
a signal operation.

In theory, we can use EXT_metal_objects and export the VkSemaphore to a
`MTLSharedEvent` but we do not have access to the `MVKSemaphoreMTLEvent` in
order to determine the `_mtlEventValue` (last encoded wait value).

This functionality is required to implement Venus on macOS which uses these
functions to synchronize the guest semaphore state to the host.
This commit is contained in:
osy
2025-11-26 15:33:42 -08:00
parent db445ff204
commit 2f1358e291
6 changed files with 91 additions and 4 deletions

View File

@@ -1839,12 +1839,16 @@ void MVKPhysicalDevice::getExternalFenceProperties(const VkPhysicalDeviceExterna
pExternalFenceProperties->pNext = next;
}
static const VkExternalSemaphoreProperties _emptyExtSemProps = {VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0};
static const VkExternalSemaphoreProperties _extSemProps = {
VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES,
nullptr, 0, 0,
VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT
};
void MVKPhysicalDevice::getExternalSemaphoreProperties(const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
void* next = pExternalSemaphoreProperties->pNext;
*pExternalSemaphoreProperties = _emptyExtSemProps;
*pExternalSemaphoreProperties = _extSemProps;
pExternalSemaphoreProperties->pNext = next;
}

View File

@@ -817,6 +817,8 @@ void MVKInstance::initProcAddrs() {
ADD_DVC_EXT_ENTRY_POINT(vkCmdSetTessellationDomainOriginEXT, EXT_EXTENDED_DYNAMIC_STATE_3);
ADD_DVC_EXT_ENTRY_POINT(vkGetMemoryMetalHandleEXT, EXT_EXTERNAL_MEMORY_METAL);
ADD_DVC_EXT_ENTRY_POINT(vkGetMemoryMetalHandlePropertiesEXT, EXT_EXTERNAL_MEMORY_METAL);
ADD_DVC_EXT_ENTRY_POINT(vkImportSemaphoreFdKHR, KHR_EXTERNAL_SEMAPHORE_FD);
ADD_DVC_EXT_ENTRY_POINT(vkGetSemaphoreFdKHR, KHR_EXTERNAL_SEMAPHORE_FD);
}
void MVKInstance::logVersions() {

View File

@@ -191,6 +191,15 @@ public:
*/
virtual id<MTLSharedEvent> getMTLSharedEvent() { return nil; };
/**
* Import a semaphore from a file descriptor
*/
virtual VkResult importFd(VkSemaphoreImportFlags flags, VkExternalSemaphoreHandleTypeFlagBits handleType, int fd) { return VK_ERROR_FEATURE_NOT_PRESENT; };
/**
* Export a semaphore to a file descriptor
*/
virtual VkResult exportFd(VkExternalSemaphoreHandleTypeFlagBits handleType, int *pFd) { return VK_ERROR_FEATURE_NOT_PRESENT; };
#pragma mark Construction
@@ -241,6 +250,8 @@ public:
uint64_t deferSignal() override;
void encodeDeferredSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t deferToken) override;
bool isUsingCommandEncoding() override { return true; }
VkResult importFd(VkSemaphoreImportFlags flags, VkExternalSemaphoreHandleTypeFlagBits handleType, int fd) override;
VkResult exportFd(VkExternalSemaphoreHandleTypeFlagBits handleType, int *pFd) override;
MVKSemaphoreMTLEvent(MVKDevice* device,
const VkSemaphoreCreateInfo* pCreateInfo,
@@ -385,7 +396,6 @@ public:
/** Returns whether this fence has been signaled and not reset. */
bool getIsSignaled();
#pragma mark Construction
MVKFence(MVKDevice* device, const VkFenceCreateInfo* pCreateInfo) :

View File

@@ -131,6 +131,48 @@ void MVKSemaphoreMTLEvent::encodeDeferredSignal(id<MTLCommandBuffer> mtlCmdBuff,
[mtlCmdBuff encodeSignalEvent: _mtlEvent value: deferToken];
}
VkResult MVKSemaphoreMTLEvent::importFd(VkSemaphoreImportFlags flags, VkExternalSemaphoreHandleTypeFlagBits handleType, int fd) {
if (handleType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT || fd != -1) {
return reportError(VK_ERROR_INVALID_EXTERNAL_HANDLE, "importFd(): Only VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT with fd == -1 is supported.");
}
/**
* If handleType is `VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT`, the special
* value -1 for fd is treated like a valid sync file descriptor referring to an object that has already
* signaled. The import operation will succeed and the VkFence will have a temporarily
* imported payload as if a valid file descriptor had been provided.
*/
/**
* If handleType is `VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT`, the special
* value -1 for fd is treated like a valid sync file descriptor referring to an object that has already
* signaled. The import operation will succeed and the VkSemaphore will have a temporarily
* imported payload as if a valid file descriptor had been provided.
*/
uint64_t value = _mtlEventValue.load();
[(id<MTLSharedEvent>)_mtlEvent setSignaledValue:value];
return VK_SUCCESS;
}
VkResult MVKSemaphoreMTLEvent::exportFd(VkExternalSemaphoreHandleTypeFlagBits handleType, int *pFd) {
if (handleType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) {
return reportError(VK_ERROR_INVALID_EXTERNAL_HANDLE, "exportFd(): Only VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT is supported.");
}
/**
* `VUID-VkSemaphoreGetFdInfoKHR-handleType-03254`
* If handleType refers to a handle type with copy payload transference semantics, semaphore
* must have an associated semaphore signal operation that has been submitted for execution
* and any semaphore signal operations on which it depends must have also been submitted
* for execution
*/
/**
* ... exporting a semaphore payload to a handle with copy transference has the same side
* effects on the source semaphores payload as executing a semaphore wait operation.
*/
uint64_t value = _mtlEventValue.load();
[(id<MTLSharedEvent>)_mtlEvent waitUntilSignaledValue:value timeoutMS:kMVKUndefinedLargeUInt64];
*pFd = -1;
return VK_SUCCESS;
}
MVKSemaphoreMTLEvent::MVKSemaphoreMTLEvent(MVKDevice* device,
const VkSemaphoreCreateInfo* pCreateInfo,
const VkExportMetalObjectCreateInfoEXT* pExportInfo,
@@ -144,7 +186,11 @@ MVKSemaphoreMTLEvent::MVKSemaphoreMTLEvent(MVKDevice* device,
_mtlEvent = [getMTLDevice() newSharedEvent]; //retained
_mtlEventValue = ((id<MTLSharedEvent>)_mtlEvent).signaledValue + 1;
} else {
_mtlEvent = [getMTLDevice() newEvent]; //retained
// We might use importFd()/exportFd() so we need a MTLSharedEvent
// At creation, we might know about potential exports when
// `VkExportSemaphoreCreateInfo` is present but we have no idea
// if imports might happen.
_mtlEvent = [getMTLDevice() newSharedEvent]; //retained
_mtlEventValue = 1;
}
}

View File

@@ -63,6 +63,7 @@ MVK_EXTENSION(KHR_external_fence_capabilities, KHR_EXTERNAL_FENCE_CAPAB
MVK_EXTENSION(KHR_external_memory, KHR_EXTERNAL_MEMORY, DEVICE, 10.11, 8.0, 1.0)
MVK_EXTENSION(KHR_external_memory_capabilities, KHR_EXTERNAL_MEMORY_CAPABILITIES, INSTANCE, 10.11, 8.0, 1.0)
MVK_EXTENSION(KHR_external_semaphore, KHR_EXTERNAL_SEMAPHORE, DEVICE, 10.11, 8.0, 1.0)
MVK_EXTENSION(KHR_external_semaphore_fd, KHR_EXTERNAL_SEMAPHORE_FD, DEVICE, 10.11, 8.0, 1.0)
MVK_EXTENSION(KHR_external_semaphore_capabilities, KHR_EXTERNAL_SEMAPHORE_CAPABILITIES, INSTANCE, 10.11, 8.0, 1.0)
MVK_EXTENSION(KHR_format_feature_flags2, KHR_FORMAT_FEATURE_FLAGS_2, DEVICE, 10.11, 8.0, 1.0)
MVK_EXTENSION(KHR_fragment_shader_barycentric, KHR_FRAGMENT_SHADER_BARYCENTRIC, DEVICE, 10.15, 14.0, 1.0)

View File

@@ -3371,6 +3371,30 @@ MVK_PUBLIC_VULKAN_SYMBOL VkResult vkGetMemoryMetalHandlePropertiesEXT(
MVK_PUBLIC_VULKAN_CORE_ALIAS(vkGetPhysicalDeviceExternalSemaphoreProperties, KHR);
#pragma mark -
#pragma mark VK_KHR_external_semaphore_fd extension
MVK_PUBLIC_VULKAN_SYMBOL VkResult vkImportSemaphoreFdKHR(
VkDevice device,
const VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo) {
MVKTraceVulkanCallStart();
auto* mvkSem4 = (MVKSemaphore*)pImportSemaphoreFdInfo->semaphore;
VkResult rslt = mvkSem4->importFd(pImportSemaphoreFdInfo->flags, pImportSemaphoreFdInfo->handleType, pImportSemaphoreFdInfo->fd);
MVKTraceVulkanCallEnd();
return rslt;
}
MVK_PUBLIC_VULKAN_SYMBOL VkResult vkGetSemaphoreFdKHR(
VkDevice device,
const VkSemaphoreGetFdInfoKHR* pGetFdInfo,
int* pFd) {
MVKTraceVulkanCallStart();
auto* mvkSem4 = (MVKSemaphore*)pGetFdInfo->semaphore;
VkResult rslt = mvkSem4->exportFd(pGetFdInfo->handleType, pFd);
MVKTraceVulkanCallEnd();
return rslt;
}
#pragma mark -
#pragma mark VK_KHR_get_memory_requirements2 extension