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);