VPI - Vision Programming Interface

4.1 Release

Lock And Extract Interop Handles

Overview

The Lock And Extract Interop Handles sample demonstrates how to create a VPI-owned image and then extract exactly one interop handle from it.

The sample supports three zero-copy export modes:

  • cuda_ptr via vpiImageLockData(..., VPI_IMAGE_BUFFER_CUDA_PITCH_LINEAR, ...)
  • nvbufsurface via vpiImageLockData(..., VPI_IMAGE_BUFFER_NVBUFFER, ...) and NvBufSurfaceFromFd
  • nvscibuf via vpiImageLockData(..., VPI_IMAGE_BUFFER_NVSCIBUF, ...)

The sample creates a default VPI context, which enables all backends supported by the running platform, reads those backend flags with vpiContextGetFlags, and passes the backend flags to vpiImageCreate instead of wrapping external memory first. This is the preferred direction when a pipeline wants VPI to own the image and then needs to hand the image to CUDA, multimedia, or NvSci code afterwards.

Instructions

This sample takes one command line parameter:

./vpi_sample_23_lock_extract_interop <cuda_ptr|nvbufsurface|nvscibuf>

Examples:

  • Export a CUDA pointer from a VPI-owned image
    ./vpi_sample_23_lock_extract_interop cuda_ptr
  • Export an NvBufSurface from a VPI-owned image
    ./vpi_sample_23_lock_extract_interop nvbufsurface
  • Export an NvSciBufObj from a VPI-owned image
    ./vpi_sample_23_lock_extract_interop nvscibuf
Note
This sample is available only on Tegra platforms.
NvBufSurface extraction through VPI_IMAGE_BUFFER_NVBUFFER is Thor-only. On Orin, use either cuda_ptr or nvscibuf.

Interop Guidance

  • Wrapping external CUDA memory or NvBufSurface memory into a VPIImage while enabling all supported backends may require internal copies.
  • Creating a VPI-owned image with the VPIBackend flags needed by the VPI stages that will consume it, then extracting a CUDA pointer, NvBufSurface, or NvSciBufObj from it with vpiImageLockData, is zero-copy when the platform and format support the requested mapping.
  • For mixed VPI/CUDA/multimedia/NvSci pipelines, prefer create in VPI first, extract afterwards.

Workflow

  1. Create a VPI-owned image with explicit backend flags. This sample passes the backend flags enabled by the default context, equivalent to creating the image with 0.
  2. Choose one export mode on the command line.
  3. Lock the image with the buffer type that matches that mode.
  4. Read the returned CUDA pointer, NvBufSurface, or NvSciBufObj handle.
  5. Unlock the image when finished using the exported handle.

Source Code

For convenience, here's the code that is also installed in the samples directory.

29 #include <nvbufsurface.h>
30 #include <nvscibuf.h>
31 #include <vpi/Context.h>
32 #include <vpi/Image.h>
33 
34 #include <cstdint>
35 #include <cstdlib>
36 #include <iostream>
37 #include <sstream>
38 #include <stdexcept>
39 #include <string>
40 
41 #define CHECK_STATUS(STMT) \
42  do \
43  { \
44  VPIStatus status = (STMT); \
45  if (status != VPI_SUCCESS) \
46  { \
47  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
48  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
49  std::ostringstream ss; \
50  ss << "line " << __LINE__ << " " << vpiStatusGetName(status) << ": " << buffer; \
51  throw std::runtime_error(ss.str()); \
52  } \
53  } while (0)
54 
55 namespace {
56 
57 enum class ExportMode
58 {
59  CUDA_PTR,
60  NVBUFSURFACE,
61  NVSCIBUF
62 };
63 
64 ExportMode ParseExportMode(int argc, char *argv[])
65 {
66  if (argc != 2)
67  {
68  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cuda_ptr|nvbufsurface|nvscibuf>");
69  }
70 
71  std::string mode = argv[1];
72 
73  if (mode == "cuda_ptr")
74  {
75  return ExportMode::CUDA_PTR;
76  }
77  if (mode == "nvbufsurface")
78  {
79  return ExportMode::NVBUFSURFACE;
80  }
81  if (mode == "nvscibuf")
82  {
83  return ExportMode::NVSCIBUF;
84  }
85 
86  throw std::runtime_error("Export mode must be one of cuda_ptr, nvbufsurface or nvscibuf");
87 }
88 
89 void PrintCudaPointer(VPIImage image)
90 {
91  VPIImageData cudaData{};
92  CHECK_STATUS(vpiImageLockData(image, VPI_LOCK_READ, VPI_IMAGE_BUFFER_CUDA_PITCH_LINEAR, &cudaData));
93 
94  const VPIImagePlanePitchLinear &plane = cudaData.buffer.pitch.planes[0];
95 
96  std::cout << "CUDA pointer export\n";
97  std::cout << " pointer : " << static_cast<void *>(plane.pBase) << "\n";
98  std::cout << " offsetBytes : " << plane.offsetBytes << "\n";
99  std::cout << " pitchBytes : " << plane.pitchBytes << "\n";
100  std::cout << " note : This export is zero-copy.\n";
101 
102  CHECK_STATUS(vpiImageUnlock(image));
103 }
104 
105 void PrintNvBufSurface(VPIImage image)
106 {
107  VPIImageData nvBufferData{};
108  CHECK_STATUS(vpiImageLockData(image, VPI_LOCK_READ, VPI_IMAGE_BUFFER_NVBUFFER, &nvBufferData));
109 
110  NvBufSurface *surface = nullptr;
111  if (NvBufSurfaceFromFd(nvBufferData.buffer.fd, reinterpret_cast<void **>(&surface)) != 0 || surface == nullptr)
112  {
113  CHECK_STATUS(vpiImageUnlock(image));
114  throw std::runtime_error("NvBufSurfaceFromFd failed");
115  }
116 
117  std::cout << "NvBufSurface export\n";
118  std::cout << " dmabuf fd : " << nvBufferData.buffer.fd << "\n";
119  std::cout << " surface pointer : " << surface << "\n";
120  std::cout << " note : This export is zero-copy.\n";
121 
122  CHECK_STATUS(vpiImageUnlock(image));
123 }
124 
125 void PrintNvSciBufObj(VPIImage image)
126 {
127  VPIImageData nvSciData{};
128  CHECK_STATUS(vpiImageLockData(image, VPI_LOCK_READ, VPI_IMAGE_BUFFER_NVSCIBUF, &nvSciData));
129 
130  std::cout << "NvSciBufObj export\n";
131  std::cout << " object handle : " << nvSciData.buffer.nvscibuf.buffer << "\n";
132  std::cout << " note : This export is zero-copy.\n";
133 
134  CHECK_STATUS(vpiImageUnlock(image));
135 }
136 
137 } // namespace
138 
139 int main(int argc, char *argv[])
140 {
141  ExportMode exportMode = ParseExportMode(argc, argv);
142 
143  std::cout
144  << "This sample shows how to extract either a CUDA pointer, an NvBufSurface, or an NvSciBufObj\n"
145  "from a VPI-owned image. These extraction paths are zero-copy.\n"
146  "The image is created with all platform-supported backend flags so VPI can choose compatible memory.\n\n";
147 
148  VPIContext context = nullptr;
149  CHECK_STATUS(vpiContextCreate(0, &context));
150  CHECK_STATUS(vpiContextSetCurrent(context));
151 
152  uint64_t imageBackends = 0;
153  CHECK_STATUS(vpiContextGetFlags(context, &imageBackends));
154  imageBackends &= VPI_BACKEND_ALL;
155 
156  VPIImage image = nullptr;
157 
158  try
159  {
160  CHECK_STATUS(vpiImageCreate(320, 240, VPI_IMAGE_FORMAT_NV12_ER, imageBackends, &image));
161 
162  if (exportMode == ExportMode::CUDA_PTR)
163  {
164  PrintCudaPointer(image);
165  }
166  else if (exportMode == ExportMode::NVBUFSURFACE)
167  {
168  PrintNvBufSurface(image);
169  }
170  else
171  {
172  PrintNvSciBufObj(image);
173  }
174  }
175  catch (...)
176  {
177  if (image != nullptr)
178  {
179  vpiImageDestroy(image);
180  }
181  if (context != nullptr)
182  {
183  vpiContextDestroy(context);
184  }
185  throw;
186  }
187 
188  vpiImageDestroy(image);
189  vpiContextDestroy(context);
190 
191  // dummy
Functions and structures for dealing with VPI contexts.
#define VPI_IMAGE_FORMAT_NV12_ER
YUV420sp 8-bit pitch-linear format with full range.
Definition: ImageFormat.h:222
Functions and structures for dealing with VPI images.
#define VPI_BACKEND_ALL
All backends.
Definition: Types.h:106
VPIStatus vpiContextCreate(uint64_t flags, VPIContext *ctx)
Create a context instance.
VPIStatus vpiContextSetCurrent(VPIContext ctx)
Sets the context for the calling thread.
void vpiContextDestroy(VPIContext ctx)
Destroy a context instance as well as all resources it owns.
VPIStatus vpiContextGetFlags(VPIContext ctx, uint64_t *flags)
Get the current context flags.
struct VPIContextImpl * VPIContext
A handle to a context.
Definition: Types.h:236
int64_t offsetBytes
Offset in bytes from pBase to the first column of the first plane row.
Definition: Image.h:137
VPIByte * pBase
Pointer to the memory buffer which contains the plane data.
Definition: Image.h:145
int32_t pitchBytes
Difference in bytes of beginning of one row and the beginning of the previous.
Definition: Image.h:134
void vpiImageDestroy(VPIImage img)
Destroy an image instance.
struct VPIImageImpl * VPIImage
A handle to an image.
Definition: Types.h:254
VPIStatus vpiImageLockData(VPIImage img, VPILockMode mode, VPIImageBufferType bufType, VPIImageData *data)
Acquires the lock on an image object and returns the image contents.
VPIStatus vpiImageCreate(int32_t width, int32_t height, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
Create an empty image instance with the specified flags.
VPIStatus vpiImageUnlock(VPIImage img)
Releases the lock on an image object.
@ VPI_IMAGE_BUFFER_CUDA_PITCH_LINEAR
CUDA-accessible with planes in pitch-linear memory layout.
Definition: Image.h:179
@ VPI_IMAGE_BUFFER_NVSCIBUF
NvSciBuf Please consult NvSciBufObj for more information.
Definition: Image.h:207
@ VPI_IMAGE_BUFFER_NVBUFFER
NvBuffer.
Definition: Image.h:200
Stores information about image characteristics and content.
Definition: Image.h:269
Represents one image plane in pitch-linear layout.
Definition: Image.h:112
@ VPI_LOCK_READ
Lock memory only for reading.
Definition: Types.h:621