Vulkan SC-NvSciSync API Usage Examples#
Following is example code to demonstrate practical usage of VK_NV_external_sci_sync2
. The example is edited for brevity, to focus on the interoperability of Vulkan SC and NvSciSync
, is incomplete for the complete Vulkan SC creation, and ignores the NvSciIPC
process. Your application must check each VkResult
and NvSciError
and API function returns, and handle any error values.
VkFence#
VkPhysicalDevice physdev = VK_NULL_HANDLE;
VkDevice dev = VK_NULL_HANDLE;
// Create VkPhysicalDevice
// ...
VkDeviceCreateInfo deviceInfo {};
// Enable VK_NV_external_sci_sync2 extension
std::vector<const char*> extNamesDev; extNamesDev.push_back(VK_NV_EXTERNAL_SCI_SYNC_2_EXTENSION_NAME);
deviceInfo.enabledExtensionCount = (uint32_t)extNamesDev.size();
deviceInfo.ppEnabledExtensionNames = extNamesDev.data();
// Initialize other parts of deviceInfo
// ...
vkCreateDevice(physdev[0], &deviceInfo, nullptr, &dev);
// Initial Phase
NvSciSyncModule scisyncModule {nullptr};
NvSciSyncAttrList attrListWaiter;
NvSciSyncModuleOpen(&scisyncModule);
// Waiter creates NvSciSyncAttrList
NvSciSyncAttrListCreate(scisyncModule, &attrListWaiter);
VkSciSyncAttributesInfoNV vkSciSyncAttributesInfo = {
.sType = VK_STRUCTURE_TYPE_SCI_SYNC_ATTRIBUTES_INFO_NV,
.pNext = nullptr,
.clientType = VK_SCI_SYNC_CLIENT_TYPE_WAITER_NV,
.primitiveType = VK_SCI_SYNC_PRIMITIVE_TYPE_FENCE_NV
};
vkGetPhysicalDeviceSciSyncAttributesNV(
physdev, &vkSciSyncAttributesInfo, *attrListWaiter);
// Signaler creates NvSciSyncAttrList
NvSciSyncAttrList attrListSignaler;
VkSciSyncAttributesInfoNV vkSciSyncAttributesInfo = {
.sType = VK_STRUCTURE_TYPE_SCI_SYNC_ATTRIBUTES_INFO_NV,
.pNext = nullptr,
.clientType = VK_SCI_SYNC_CLIENT_TYPE_SIGNALER_NV,
.primitiveType = VK_SCI_SYNC_PRIMITIVE_TYPE_FENCE_NV
};
vkGetPhysicalDeviceSciSyncAttributesNV(
physdev, &vkSciSyncAttributesInfo, *attrListWaiter);
// Using NvSciIPC to import/export NvSciSyncAttrList
// ...
// Signaler reconcile the NvSciSyncAttrList
NvSciSyncAttrList reconciledList;
NvSciSyncAttrList unreconciledList[2] = {attrListWaiter, attrListSignaler};
NvSciSyncAttrList conflictList;
NvSciSyncAttrListReconcile(unreconciledList, 2, &reconciledList,&conflictList);
// Signaler allocate NvSciSyncObj and import to VkFence
NvSciSyncObj signalerSciSyncObj = nullptr;
VkFence signalerVkFence = VK_NULL_HANDLE;
NvSciSyncObjAlloc(reconciledList, &signalerSciSyncObj);
VkFenceCreateInfo fenceCreateInfo = {};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
vkCreateFence(dev, &fenceCreateInfo, nullptr, &signalerVkFence);
VkImportFenceSciSyncInfoNV importSciSyncInfo = {
.sType = VK_STRUCTURE_TYPE_IMPORT_FENCE_SCI_SYNC_INFO_NV;
.pNext = nullptr;
.fence = signalerVkFence;
.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SCI_SYNC_OBJ_BIT_NV
.handle = signalerSciSyncObj;
};
vkImportFenceSciSyncObjNV(dev, &importSciSyncInfo);
// Export signalerSciSyncObj via NvSciIPC
// ...
NvSciSyncObj waiterSciSyncObj;
// Waiter Import waiterSciSyncObj via NvSciIPC
// ...
// Import to VkFence
VkFence waiterVkFence;
VkFenceCreateInfo fenceCreateInfo = {};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
vkCreateFence(dev, &fenceCreateInfo, nullptr, &signalerVkFence);VkImportFenceSciSyncInfoNV importSciSyncInfo = {
.sType = VK_STRUCTURE_TYPE_IMPORT_FENCE_SCI_SYNC_INFO_NV;
.pNext = nullptr;
.fence = waiterVkFence;
.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SCI_SYNC_OBJ_BIT_NV
.handle = waiterSciSyncObj;
};
vkImportFenceSciSyncObjNV(dev, &importSciSyncInfo);
// Run-Time Phase
// Signaler signals VkFence
VkSubmitInfo submitInfo = {};
vkQueueSubmit(vksc_queue, 1, &submitInfo, signalerVkFence);
VkFenceGetSciSyncInfoNV getSciSyncInfo = {
.sType = VK_STRUCTURE_TYPE_FENCE_GET_SCI_SYNC_INFO_NV;
.pNext = nullptr;
.fence = signalerVkFence,
.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SCI_SYNC_FENCE_BIT_NV,
};
NvSciSyncFence signalerSciSyncFence {NvSciSyncFenceInitializer};
vkGetFenceSciSyncFenceNV(dev, &getSciSyncInfo, &signalerSciSyncFence);
// Export signalerSciSyncFence via NvSciIPC
// ...
// Waiter waits VkFence
NvSciSyncFence waiterSciSyncFence {NvSciSyncFenceInitializer};
// Waiter imports waiterSciSyncFence via NvSciIPC
// ...
VkImportFenceSciSyncInfoNV importSciSyncInfo = {
.sType = VK_STRUCTURE_TYPE_IMPORT_FENCE_SCI_SYNC_INFO_NV;
.pNext = nullptr;
.fence = waiterVkFence;
.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SCI_SYNC_FENCE_BIT_NV
.handle = waiterSciSyncFence;
};
vkImportFenceSciSyncFenceNV(dev, &importSciSyncInfo);
uint64_t timeout = 1000000LLU;
vkWaitForFences(dev, 1, waiterVkFence, timeout);
// Deinit Phase
NvSciSyncAttrListFree(reconciledList);
NvSciSyncAttrListFree(conflictList);
NvSciSyncAttrListFree(attrListWaiter);
NvSciSyncAttrListFree(attrListSignaler);
NvSciSyncModuleClose(scisyncModule);
vkDestroyFence(waiterVkFence);
vkDestroyFence(signalerVkFence);
vkDestroyDevice(dev, nullptr);
VkSemaphore#
VkPhysicalDevice physdev = VK_NULL_HANDLE;
VkDevice dev = VK_NULL_HANDLE;
// Create VkPhysicalDevice
// ...
VkDeviceCreateInfo deviceInfo {};
// Enable VK_NV_external_sci_sync2 extension
std::vector<const char*> extNamesDev; extNamesDev.push_back(VK_NV_EXTERNAL_SCI_SYNC_2_EXTENSION_NAME);
deviceInfo.enabledExtensionCount = (uint32_t)extNamesDev.size();
deviceInfo.ppEnabledExtensionNames = extNamesDev.data();
// Initialize other parts of deviceInfo
// ...
vkCreateDevice(physdev[0], &deviceInfo, nullptr, &dev);
// Initial Phase
NvSciSyncModule scisyncModule {nullptr};
NvSciSyncAttrList attrListWaiter;
NvSciSyncModuleOpen(&scisyncModule);
// Waiter creates NvSciSyncAttrList
NvSciSyncAttrListCreate(scisyncModule, &attrListWaiter);
VkSciSyncAttributesInfoNV vkSciSyncAttributesInfo = {
.sType = VK_STRUCTURE_TYPE_SCI_SYNC_ATTRIBUTES_INFO_NV,
.pNext = nullptr,
.clientType = VK_SCI_SYNC_CLIENT_TYPE_WAITER_NV,
.primitiveType = VK_SCI_SYNC_PRIMITIVE_TYPE_SEMAPHORE_NV
};
vkGetPhysicalDeviceSciSyncAttributesNV(
physdev, &vkSciSyncAttributesInfo, *attrListWaiter);
// Signaler creates NvSciSyncAttrList
NvSciSyncAttrList attrListSignaler;
VkSciSyncAttributesInfoNV vkSciSyncAttributesInfo = {
.sType = VK_STRUCTURE_TYPE_SCI_SYNC_ATTRIBUTES_INFO_NV,
.pNext = nullptr,
.clientType = VK_SCI_SYNC_CLIENT_TYPE_SIGNALER_NV,
.primitiveType = VK_SCI_SYNC_PRIMITIVE_TYPE_SEMAPHORE_NV
};
vkGetPhysicalDeviceSciSyncAttributesNV(
physdev, &vkSciSyncAttributesInfo, *attrListWaiter);
// Using NvSciIPC to import/export NvSciSyncAttrList
// ...
// Signaler reconcile the NvSciSyncAttrList
NvSciSyncAttrList reconciledList;
NvSciSyncAttrList unreconciledList[2] = {attrListWaiter, attrListSignaler};
NvSciSyncAttrList conflictList;
NvSciSyncAttrListReconcile(unreconciledList, 2, &reconciledList,&conflictList);
// Signaler allocate NvSciSyncObj and import to VkSemaphoreSciSyncPoolNV
NvSciSyncObj signalerSciSyncObj;
NvSciSyncObjAlloc(reconciledList, &signalerSciSyncObj);
VkSemaphoreSciSyncPoolCreateInfoNV signalerInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SCI_SYNC_POOL_CREATE_INFO_NV;
.pNext = nullptr;
.handle = signalerSciSyncObj
};
VkSemaphoreSciSyncPoolNV signalerSemPool = VK_NULL_HANDLE;
vkCreateSemaphoreSciSyncPoolNV(dev, &signalerInfo, nullptr, &signalerSemPool);
// Export signalerSciSyncObj via NvSciIPC
// ...
NvSciSyncObj waiterSciSyncObj;
// Waiter Import waiterSciSyncObj via NvSciIPC
// ...
VkSemaphoreSciSyncPoolCreateInfoNV waiterInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SCI_SYNC_POOL_CREATE_INFO_NV;
.pNext = nullptr;
.handle = waiterSciSyncObj
};
VkSemaphoreSciSyncPoolNV waiterSemPool = VK_NULL_HANDLE;
vkCreateSemaphoreSciSyncPoolNV(dev, &waiterInfo, nullptr, &signalerSemPool);
// Run-time Phase
// Signaler signals VkSemaphore
uint32_t const fenceId = 0;
uint32_t signalValue = 0;
// Application tracks and maintains the signal value
signalValue = signalVal + 1U;
NvSciSyncFence signalerFence{};
NvSciSyncFenceUpdateFence(signalerSciSyncObj, fenceId, signalValue, &signalerFence);
// Retrieve VkSemaphore from the VkSemaphoreSciSyncPoolNV providing
// NvSciSyncFence
VkSemaphoreSciSyncCreateInfoNV semaphoreSciSyncInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SCI_SYNC_CREATE_INFO_NV,
.pNext = nullptr,
.semaphorePool = signalerSemPool,
.pFence = signalerFence
};
// Only timeline semaphore supports NvSciSync interops
VkSemaphoreTypeCreateInfo sempahoreTypeInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.pNext = &semaphoreSciSyncInfo,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
.initialValue = 0 // initialValue must be 0 with NvSciSyncObj
};
VkSemaphoreCreateInfo semaphoreCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &sempahoreTypeInfo,
.flags = 0, // reserved bit, must be zero
};
VkSemaphore signalerSemaphore;
vkCreateSemaphore(dev, &semaphoreCreateInfo, nullptr, &signalerSemaphore);
// GPU Signal
if (GPU_SIGNAL) {
VkTimelineSemaphoreSubmitInfo semaphoreInfo = {
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreValueCount = 0,
.pWaitSemaphoreValues = nullptr,
.signalSemaphoreValueCount = 1,
.pSignalSemaphoreValues = &signalValue
};
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = &semaphoreInfo,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 0,
.pCommandBuffers = nullptr,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &signalerSemaphore,
};
vkQueueSubmit(m_queue, 1, &submitInfo, nullptr);
}
else if (CPU_SIGNAL) {
VkSemaphoreSignalInfo signalInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO,
.pNext = nullptr,
.semaphore = signalerSemaphore,
.value = signalValue
};
vkSignalSemaphore(dev, &signalInfo);
}
// Export signalerFence via NvSciIPC
// ...
// Recycle the VkSemaphore back to the pool
vkDestroySemaphore(dev, signalerSemaphore, nullptr);
// Waiter waits VkSemaphore
NvSciSyncFence waiterFence{};
// Import waiterFence via NvSciIPC
// ...
uint32_t const fenceId = 0;
uint32_t waitValue = 0;
NvSciSyncFenceExtractFence(&waiterFence, &fenceId, &waitValue);
// Retreive VkSemaphore from the VkSemaphoreSciSyncPoolNV providing
// NvSciSyncFence
VkSemaphoreSciSyncCreateInfoNV semaphoreSciSyncInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SCI_SYNC_CREATE_INFO_NV,
.pNext = nullptr,
.semaphorePool = waiterSemPool,
.pFence = waiterFence
};
// Only timeline semaphore supports NvSciSync interops
VkSemaphoreTypeCreateInfo sempahoreTypeInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.pNext = &semaphoreSciSyncInfo,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
.initialValue = 0 // initialValue must be 0 with NvSciSyncObj
};
VkSemaphoreCreateInfo semaphoreCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &sempahoreTypeInfo,
.flags = 0, // reserved bit, must be zero
};
VkSemaphore waiterSemaphore;
vkCreateSemaphore(dev, &semaphoreCreateInfo, nullptr, &waiterSemaphore);
if (GPU_WAIT) {
VkTimelineSemaphoreSubmitInfo semaphoreInfo = {
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreValueCount = 1,
.pWaitSemaphoreValues = &waitValue,
.signalSemaphoreValueCount = 0,
.pSignalSemaphoreValues = nullptr
};
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = &semaphoreInfo,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &waiterSemaphore,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 0,
.pCommandBuffers = nullptr,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
vkQueueSubmit(m_queue, 1, &submitInfo, nullptr);
}
else if (CPU_WAIT) {
VkSemaphoreWaitInfo waitInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
.pNext = nullptr,
.flags = 0,
.semaphoreCount = 1,
.pSemaphores = &waiterSemaphore,
.pValues = &value
};
uint64_t timeout = 1000000LLU;
vkWaitSemaphores(m_dev, &waitInfo, timeout);
}
// Recycle the VkSemaphore back to the pool
vkDestroySemaphore(dev, vkWaitSemaphores, nullptr);
VkSemaphore on Deterministic Fence#
// Skip the Init-time part, assume Deterministic Fence is enabled
// ...
// Run-time Phase
// Signaler signals VkSemaphore
uint32_t const fenceId = 0;
uint32_t signalValue = 0;
while (true) {
// Application tracks and maintains the signal value, it will always increment
// by 1 on the signal value.
signalValue = signalVal + 1U;
NvSciSyncFence signalerFence{};
NvSciSyncFenceUpdateFence(signalerSciSyncObj, fenceId, signalValue, &signalerFence);
// Retreive VkSemaphore from the VkSemaphoreSciSyncPoolNV providing
// NvSciSyncFence
VkSemaphoreSciSyncCreateInfoNV semaphoreSciSyncInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SCI_SYNC_CREATE_INFO_NV,
.pNext = nullptr,
.semaphorePool = signalerSemPool,
.pFence = signalerFence
};
// Only timeline semaphore supports NvSciSync interops
VkSemaphoreTypeCreateInfo sempahoreTypeInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.pNext = &semaphoreSciSyncInfo,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
.initialValue = 0 // initialValue must be 0 with NvSciSyncObj
};
VkSemaphoreCreateInfo semaphoreCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &sempahoreTypeInfo,
.flags = 0, // reserved bit, must be zero
};
VkSemaphore signalerSemaphore;
vkCreateSemaphore(dev, &semaphoreCreateInfo, nullptr, &signalerSemaphore);
// Only show the GPU Signal path
VkTimelineSemaphoreSubmitInfo semaphoreInfo = {
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreValueCount = 0,
.pWaitSemaphoreValues = nullptr,
.signalSemaphoreValueCount = 1,
.pSignalSemaphoreValues = &signalValue
};
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = &semaphoreInfo,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 0,
.pCommandBuffers = nullptr,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &signalerSemaphore,
};
vkQueueSubmit(m_queue, 1, &submitInfo, nullptr);
// Recycle the VkSemaphore back to the pool
vkDestroySemaphore(dev, signalerSemaphore, nullptr);
}
// Waiter waits VkSemaphore
uint32_t const fenceId = 0;
uint32_t waitValue = 0;
while(true) {
// Applications constructs a NvSciSyncFence themselves insead of importing from
// NvSciIPC
waitValue = waitValue + 1U;
NvSciSyncFence waiterFence{};
NvSciSyncFenceUpdateFence(waiterSciSyncObj, fenceId, waitValue, &waiterFence);
// Retreive VkSemaphore from the VkSemaphoreSciSyncPoolNV providing
// NvSciSyncFence
VkSemaphoreSciSyncCreateInfoNV semaphoreSciSyncInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SCI_SYNC_CREATE_INFO_NV,
.pNext = nullptr,
.semaphorePool = waiterSemPool,
.pFence = waiterFence
};
// Only timeline semaphore supports NvSciSync interops
VkSemaphoreTypeCreateInfo sempahoreTypeInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.pNext = &semaphoreSciSyncInfo,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
.initialValue = 0 // initialValue must be 0 with NvSciSyncObj
};
VkSemaphoreCreateInfo semaphoreCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &sempahoreTypeInfo,
.flags = 0, // reserved bit, must be zero
};
VkSemaphore waiterSemaphore;
vkCreateSemaphore(dev, &semaphoreCreateInfo, nullptr, &waiterSemaphore);
VkTimelineSemaphoreSubmitInfo semaphoreInfo = {
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreValueCount = 1,
.pWaitSemaphoreValues = &waitValue,
.signalSemaphoreValueCount = 0,
.pSignalSemaphoreValues = nullptr
};
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = &semaphoreInfo,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &waiterSemaphore,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 0,
.pCommandBuffers = nullptr,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
vkQueueSubmit(m_queue, 1, &submitInfo, nullptr);
// Recycle the VkSemaphore back to the pool
vkDestroySemaphore(dev, vkWaitSemaphores, nullptr);
}