Vulkan SC-NvSciBuf Interop Examples
This section provides example code to demonstrate practical usage of
VK_NV_external_memory_sci_buf
. The example is edited for brevity, to focus
on the interoperability of Vulkan SC and NvSciBuf
, and is incomplete for the
complete Vulkan SC creation. Your application must check each VkResult
and
NvSciError
, and API function returns, and handle error values.
VkBuffer - NvSciBuf
// Create VkBuffer
VkBuffer vkbuffer = VK_NULL_HANDLE;
const uint32_t queueFamilyIndex = 0;
VkExternalMemoryBufferCreateInfo externalMemoryBufferInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
.pNext = nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_SCI_BUF_BIT_NV
};
VkDeviceSize size = 256;
VkBufferCreateInfo bufferInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = &externalMemoryBufferInfo,
.flags = 0,
.size = size,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 1,
.pQueueFamilyIndices = &queueFamilyIndex,
};
vkCreateBuffer(m_dev, &bufferInfo, nullptr, &vkbuffer);
VkMemoryRequirements memReqs = {};
vkGetBufferMemoryRequirements(dev, vkbuffer, &memReqs);
uint64_t alignment = memReqs.alignment;
bool needCpuAccess = false;
bool needCpuCached = false;
NvSciBufType bufType = NvSciBufType_RawBuffer;
NvSciBufAttrValAccessPerm perm = NvSciBufAccessPerm_ReadWrite;
needCpuAccess = (memReqs.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
needCpuCached = (memReqs.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
NvSciBufAttrKeyValuePair pairArray[] = {
{ NvSciBufRawBufferAttrKey_Size, (void*) &memReqs.size, sizeof(memReqs.size) },
{ NvSciBufRawBufferAttrKey_Align, (void*) &alignment, sizeof(alignment) },
{ NvSciBufGeneralAttrKey_Types, (void*) &bufType, sizeof(bufType) },
{ NvSciBufGeneralAttrKey_NeedCpuAccess, (void*) &needCpuAccess, sizeof(needCpuAccess) },
{ NvSciBufGeneralAttrKey_EnableCpuCache, (void*) &needCpuCached, sizeof(needCpuCached) },
{ NvSciBufGeneralAttrKey_RequiredPerm, (void*) &perm, sizeof(perm) },
};
NvSciBufAttrList attrList, reconciledList, conflictList;
NvSciBufAttrListCreate(scibufModule, &attrList)
// Application fills the public key-value pairs
NvSciBufAttrListSetAttrs(attrList, pairArray, sizeof(pairArray)/sizeof(NvSciBufAttrKeyValuePair));
// Driver will fill the NvSciBufGeneralAttrKey_GpuId
vkGetPhysicalDeviceSciBufAttributesNV(m_physDev, attrList);
// Allocate NvSciBufObj
NvSciBufAttrListReconcile(&attrList, 1, &reconciledList, &conflictList);
NvSciBufObj sciBufObjVkBuffer {nullptr};
NvSciBufObjAlloc(reconciledList, &sciBufObjVkBuffer);
// Create VkDeviceMemory
VkImportMemorySciBufInfoNV importSciBufInfo {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_SCI_BUF_INFO_NV;
.pNext = nullptr;
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_SCI_BUF_BIT_NV;
.handle = sciBufObjVkBuffer;
};
// GetMemoryType is a helper function to convert memoryTypeBits to memoryType index,
// skip the detail of implementation here.
VkMemoryAllocateInfo memAllocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
.pNext = importSciBufInfo;
.memoryTypeIndex = needCpuAccess ?
GetMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT):
GetMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
.allocationSize = 0; // ignore the allocate size when importing NvSciBufObj
};
VkDeviceMemory devMemory;
vkAllocateMemory(dev, &memAllocInfo, NULL, &devMemory);
vkBindBufferMemory(dev, vkbuffer, devMemory);
// De-init
vkDestroyBuffer(dev, vkbuffer, 0);
NvSciBufAttrListFree(attrList);
NvSciBufAttrListFree(reconciledList);
NvSciBufAttrListFree(conflictList);
NvSciBufObjFree(sciBufObjVkBuffer);
VkImage - NvSciBuf
// Create VkImage
VkExternalMemoryImageCreateInfo externalMemInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.pNext = nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_SCI_BUF_BIT_NV
};
VkImageCreateInfo imageInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = &externalMemInfo,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = VK_FORMAT_B8G8R8A8_UNORM,
.extent = {128, 128, 1},
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_LINEAR,
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
};
VkImage vkimage = VK_NULL_HANDLE;
vkCreateImage(m_dev, &imageInfo, NULL, &vkimage);
VkMemoryRequirements* memReqs
vkGetImageMemoryRequirements(m_dev, vkimage, memReqs);
// Fill the NvSciBufAttrList based on VkImage's attributes
NvSciBufType bufType = NvSciBufType_Image;
NvSciBufAttrValAccessPerm perm = NvSciBufAccessPerm_ReadWrite; NvSciBufAttrValImageLayoutType layout = NvSciBufImage_BlockLinearType;
NvSciBufAttrValImageScanType planescantype = NvSciBufScan_ProgressiveType;
uint32_t planeCount = 1;
NvSciBufAttrValColorFmt colorFormat = NvSciColor_A8R8G8B8;
uint32_t height = imageInfo.extent.height;
uint32_t width = imageInfo.extent.width;
bool needCpuAccess = (memReqs.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
bool needCpuCached = false;
needCpuAccess = (memReqs.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
needCpuCached = (memReqs.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
std::vector<NvSciBufAttrKeyValuePair> pairArray = {
{ NvSciBufImageAttrKey_Layout, (void*) &layout, sizeof(layout) },
{ NvSciBufImageAttrKey_PlaneCount, (void*) &planeCount, sizeof(planeCount) },
{ NvSciBufImageAttrKey_PlaneColorFormat, (void*) &colorFormat, sizeof(colorFormat) },
{ NvSciBufImageAttrKey_PlaneHeight, (void*) &height, sizeof(height) },
{ NvSciBufImageAttrKey_PlaneWidth, (void*) &width, sizeof(width) },
{ NvSciBufImageAttrKey_PlaneScanType, (void*) &planescantype, sizeof(planescantype) },
{ NvSciBufGeneralAttrKey_Types, (void*) &bufType, sizeof(bufType) },
{ NvSciBufGeneralAttrKey_NeedCpuAccess, (void*) &needCpuAccess, sizeof(needCpuAccess) },
{ NvSciBufGeneralAttrKey_EnableCpuCache, (void*) &needCpuCached, sizeof(needCpuCached) },
{ NvSciBufGeneralAttrKey_RequiredPerm, (void*) &perm, sizeof(perm) },
};
NvSciBufAttrList attrList, reconciledList, conflictList;
NvSciBufAttrListCreate(scibufModule, &attrList);
// Application fills the public key-value pairs
NvSciBufAttrListSetAttrs(*attrList, pairArray.data(), pairArray.size());
vkGetPhysicalDeviceSciBufAttributesNV(physDev, *attrList);
// Allocate NvSciBufObj
NvSciBufAttrListReconcile(&attrList, 1, &reconciledList, &conflictList);
NvSciBufObj sciBufObjVkImage {nullptr};
NvSciBufObjAlloc(reconciledList, &sciBufObjVkImage);
// Create VkDeviceMemory
VkImportMemorySciBufInfoNV importSciBufInfo {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_SCI_BUF_INFO_NV;
.pNext = nullptr;
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_SCI_BUF_BIT_NV;
.handle = sciBufObjVkImage;
};
// GetMemoryType is a helper function to convert memoryTypeBits to memoryType index,
// skip the detail of implementation here.
VkMemoryAllocateInfo memAllocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
.pNext = importSciBufInfo;
.memoryTypeIndex = needCpuAccess ?
GetMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT):
GetMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
.allocationSize = 0; // ignore the allocate size when importing NvSciBufObj
};
VkDeviceMemory devMemory;
vkAllocateMemory(dev, &memAllocInfo, NULL, &devMemory);
vkBindImageMemory(dev, sciBufObjVkImage, devMemory, 0);
// De-init
vkDestroyImage(dev, vkimage, 0);
NvSciBufAttrListFree(attrList);
NvSciBufAttrListFree(reconciledList);
NvSciBufAttrListFree(conflictList);
NvSciBufObjFree(sciBufObjVkImage);