mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2024-12-01 10:09:18 +08:00
424 lines
13 KiB
C++
424 lines
13 KiB
C++
|
/* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
* * Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* * Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||
|
* contributors may be used to endorse or promote products derived
|
||
|
* from this software without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
//
|
||
|
// DESCRIPTION: Common egl stream functions
|
||
|
//
|
||
|
|
||
|
#include "eglstrm_common.h"
|
||
|
|
||
|
EGLStreamKHR g_producerEglStream = EGL_NO_STREAM_KHR;
|
||
|
EGLStreamKHR g_consumerEglStream = EGL_NO_STREAM_KHR;
|
||
|
EGLDisplay g_producerEglDisplay = EGL_NO_DISPLAY;
|
||
|
EGLDisplay g_consumerEglDisplay = EGL_NO_DISPLAY;
|
||
|
int cudaDevIndexProd = -1;
|
||
|
int cudaDevIndexCons = -1;
|
||
|
|
||
|
#if defined(EXTENSION_LIST)
|
||
|
EXTENSION_LIST(EXTLST_DECL)
|
||
|
typedef void (*extlst_fnptr_t)(void);
|
||
|
static struct {
|
||
|
extlst_fnptr_t *fnptr;
|
||
|
char const *name;
|
||
|
bool is_dgpu; // This function is need only for dgpu case
|
||
|
} extensionList[] = {EXTENSION_LIST(EXTLST_ENTRY)};
|
||
|
|
||
|
int eglSetupExtensions(bool isCrossDevice) {
|
||
|
unsigned int i;
|
||
|
|
||
|
for (i = 0; i < (sizeof(extensionList) / sizeof(*extensionList)); i++) {
|
||
|
// load the dgpu function only if we are running cross device test
|
||
|
if ((!extensionList[i].is_dgpu) ||
|
||
|
(extensionList[i].is_dgpu == isCrossDevice)) {
|
||
|
*extensionList[i].fnptr = eglGetProcAddress(extensionList[i].name);
|
||
|
if (*extensionList[i].fnptr == NULL) {
|
||
|
printf("Couldn't get address of %s()\n", extensionList[i].name);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int EGLStreamInit(bool isCrossDevice, int isConsumer,
|
||
|
EGLNativeFileDescriptorKHR fileDesc) {
|
||
|
static const EGLint streamAttrFIFOMode[] = {
|
||
|
EGL_STREAM_FIFO_LENGTH_KHR, 5, EGL_SUPPORT_REUSE_NV, EGL_FALSE, EGL_NONE};
|
||
|
EGLDisplay eglDisplay[2] = {0};
|
||
|
EGLStreamKHR eglStream[2] = {0};
|
||
|
EGLBoolean eglStatus;
|
||
|
|
||
|
#define MAX_EGL_DEVICES 4
|
||
|
|
||
|
EGLDeviceEXT devices[MAX_EGL_DEVICES];
|
||
|
EGLint numDevices = 0;
|
||
|
|
||
|
eglStatus = eglQueryDevicesEXT(MAX_EGL_DEVICES, devices, &numDevices);
|
||
|
if (eglStatus != EGL_TRUE) {
|
||
|
printf("Error querying EGL devices\n");
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
if (numDevices == 0) {
|
||
|
printf("No EGL devices found\n");
|
||
|
eglStatus = EGL_FALSE;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
// If cross device, create discrete GPU stream first and then create the
|
||
|
// integrated GPU stream to connect to it via fd. The other way round fails
|
||
|
// in producer connect.
|
||
|
//
|
||
|
// TODO: Find out if this EGL behavior is by design.
|
||
|
if (isConsumer) {
|
||
|
int egl_device_id = 0;
|
||
|
for (egl_device_id = 0; egl_device_id < numDevices; egl_device_id++) {
|
||
|
EGLAttrib cuda_device;
|
||
|
eglStatus = eglQueryDeviceAttribEXT(devices[egl_device_id],
|
||
|
EGL_CUDA_DEVICE_NV, &cuda_device);
|
||
|
if (eglStatus == EGL_TRUE) {
|
||
|
cudaDevIndexCons = cuda_device; // We select first EGL-CUDA Capable
|
||
|
// device for consumer.
|
||
|
printf(
|
||
|
"Found EGL-CUDA Capable device with CUDA Device id = %d out of "
|
||
|
"egl_device_id = %d\n",
|
||
|
(int)cudaDevIndexCons, egl_device_id);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (egl_device_id >= numDevices) {
|
||
|
printf("No CUDA Capable EGL Device found.. Waiving execution\n");
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
g_consumerEglDisplay = eglGetPlatformDisplayEXT(
|
||
|
EGL_PLATFORM_DEVICE_EXT, (void *)devices[egl_device_id], NULL);
|
||
|
if (g_consumerEglDisplay == EGL_NO_DISPLAY) {
|
||
|
printf("Could not get EGL display from device. \n");
|
||
|
eglStatus = EGL_FALSE;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
eglStatus = eglInitialize(g_consumerEglDisplay, 0, 0);
|
||
|
if (!eglStatus) {
|
||
|
printf("EGL failed to initialize. \n");
|
||
|
eglStatus = EGL_FALSE;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
g_consumerEglStream =
|
||
|
eglCreateStreamKHR(g_consumerEglDisplay, streamAttrFIFOMode);
|
||
|
if (g_consumerEglStream == EGL_NO_STREAM_KHR) {
|
||
|
printf("Could not create EGL stream.\n");
|
||
|
eglStatus = EGL_FALSE;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
eglStatus = eglStreamAttribKHR(g_consumerEglDisplay, g_consumerEglStream,
|
||
|
EGL_CONSUMER_LATENCY_USEC_KHR, 16000);
|
||
|
if (eglStatus != EGL_TRUE) {
|
||
|
printf("eglStreamAttribKHR EGL_CONSUMER_LATENCY_USEC_KHR failed\n");
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
eglStatus =
|
||
|
eglStreamAttribKHR(g_consumerEglDisplay, g_consumerEglStream,
|
||
|
EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR, 16000);
|
||
|
if (eglStatus != EGL_TRUE) {
|
||
|
printf(
|
||
|
"eglStreamAttribKHR EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR failed\n");
|
||
|
goto Done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!isConsumer) { // Producer
|
||
|
|
||
|
if (fileDesc == EGL_NO_FILE_DESCRIPTOR_KHR) {
|
||
|
printf("Cuda Producer received bad file descriptor\n");
|
||
|
eglStatus = EGL_FALSE;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
int egl_device_id = 0;
|
||
|
int egl_cuda_devices = 0;
|
||
|
for (egl_device_id = 0; egl_device_id < numDevices; egl_device_id++) {
|
||
|
EGLAttrib cuda_device = -1;
|
||
|
eglStatus = eglQueryDeviceAttribEXT(devices[egl_device_id],
|
||
|
EGL_CUDA_DEVICE_NV, &cuda_device);
|
||
|
if (eglStatus == EGL_TRUE) {
|
||
|
egl_cuda_devices++;
|
||
|
if (isCrossDevice && (egl_cuda_devices > 1)) {
|
||
|
// We select second EGL-CUDA Capable device for producer.
|
||
|
cudaDevIndexProd = (int)cuda_device;
|
||
|
printf(
|
||
|
"Found EGL-CUDA Capable device with CUDA Device id = %d "
|
||
|
"egl_device_id = %d \n",
|
||
|
(int)cudaDevIndexProd, egl_device_id);
|
||
|
break;
|
||
|
}
|
||
|
if (!isCrossDevice) {
|
||
|
// We select first EGL-CUDA Capable device for producer same as
|
||
|
// consumer.
|
||
|
cudaDevIndexProd = (int)cuda_device;
|
||
|
printf(
|
||
|
"Found EGL-CUDA Capable device with CUDA Device id = %d "
|
||
|
"egl_device_id = %d \n",
|
||
|
(int)cudaDevIndexProd, egl_device_id);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (egl_device_id >= numDevices) {
|
||
|
printf("No CUDA Capable EGL Device found.. Waiving execution\n");
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
g_producerEglDisplay = eglGetPlatformDisplayEXT(
|
||
|
EGL_PLATFORM_DEVICE_EXT, (void *)devices[egl_device_id], NULL);
|
||
|
if (g_producerEglDisplay == EGL_NO_DISPLAY) {
|
||
|
printf("Could not get EGL display from device. \n");
|
||
|
eglStatus = EGL_FALSE;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
eglStatus = eglInitialize(g_producerEglDisplay, 0, 0);
|
||
|
if (!eglStatus) {
|
||
|
printf("EGL failed to initialize. \n");
|
||
|
eglStatus = EGL_FALSE;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
g_producerEglStream =
|
||
|
eglCreateStreamFromFileDescriptorKHR(g_producerEglDisplay, fileDesc);
|
||
|
close(fileDesc);
|
||
|
|
||
|
if (g_producerEglStream == EGL_NO_STREAM_KHR) {
|
||
|
printf("CUDA Producer Could not create EGL stream.\n");
|
||
|
eglStatus = EGL_FALSE;
|
||
|
goto Done;
|
||
|
} else {
|
||
|
printf("Producer created EGLStream for the GPU.\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Done:
|
||
|
return eglStatus == EGL_TRUE ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
void EGLStreamFini(void) {
|
||
|
if (g_producerEglStream != EGL_NO_STREAM_KHR) {
|
||
|
eglDestroyStreamKHR(g_producerEglDisplay, g_producerEglStream);
|
||
|
}
|
||
|
if (g_consumerEglStream != g_producerEglStream) {
|
||
|
if (g_consumerEglStream != EGL_NO_STREAM_KHR) {
|
||
|
eglDestroyStreamKHR(g_consumerEglDisplay, g_consumerEglStream);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int UnixSocketConnect(const char *socket_name) {
|
||
|
int sock_fd = -1;
|
||
|
struct sockaddr_un sock_addr;
|
||
|
int wait_loop = 0;
|
||
|
|
||
|
sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
||
|
if (sock_fd < 0) {
|
||
|
printf("%s: socket create failed.\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (verbose) printf("%s: send_fd: sock_fd: %d\n", __func__, sock_fd);
|
||
|
|
||
|
memset(&sock_addr, 0, sizeof(struct sockaddr_un));
|
||
|
sock_addr.sun_family = AF_UNIX;
|
||
|
strncpy(sock_addr.sun_path, socket_name, sizeof(sock_addr.sun_path) - 1);
|
||
|
|
||
|
while (connect(sock_fd, (const struct sockaddr *)&sock_addr,
|
||
|
sizeof(struct sockaddr_un))) {
|
||
|
if (wait_loop < 60) {
|
||
|
if (!wait_loop)
|
||
|
printf("Waiting for EGL stream producer ");
|
||
|
else
|
||
|
printf(".");
|
||
|
fflush(stdout);
|
||
|
sleep(1);
|
||
|
wait_loop++;
|
||
|
} else {
|
||
|
printf("\n%s: Waiting timed out\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
if (wait_loop) printf("\n");
|
||
|
|
||
|
if (verbose) printf("%s: Wait is done\n", __func__);
|
||
|
|
||
|
return sock_fd;
|
||
|
}
|
||
|
|
||
|
/* Send <fd_to_send> (a file descriptor) to another process */
|
||
|
/* over a unix domain socket named <socket_name>. */
|
||
|
/* <socket_name> can be any nonexistant filename. */
|
||
|
int EGLStreamSendfd(int send_fd, int fd_to_send) {
|
||
|
struct msghdr msg;
|
||
|
struct iovec iov[1];
|
||
|
char ctrl_buf[CMSG_SPACE(sizeof(int))];
|
||
|
struct cmsghdr *cmsg = NULL;
|
||
|
void *data;
|
||
|
int res;
|
||
|
memset(&msg, 0, sizeof(msg));
|
||
|
|
||
|
iov[0].iov_len = 1; // must send at least 1 byte
|
||
|
iov[0].iov_base = (void *)"x"; // any byte value (value ignored)
|
||
|
msg.msg_iov = iov;
|
||
|
msg.msg_iovlen = 1;
|
||
|
|
||
|
memset(ctrl_buf, 0, sizeof(ctrl_buf));
|
||
|
msg.msg_control = ctrl_buf;
|
||
|
msg.msg_controllen = sizeof(ctrl_buf);
|
||
|
|
||
|
cmsg = CMSG_FIRSTHDR(&msg);
|
||
|
cmsg->cmsg_level = SOL_SOCKET;
|
||
|
cmsg->cmsg_type = SCM_RIGHTS;
|
||
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||
|
data = CMSG_DATA(cmsg);
|
||
|
*(int *)data = fd_to_send;
|
||
|
|
||
|
msg.msg_controllen = cmsg->cmsg_len;
|
||
|
|
||
|
res = sendmsg(send_fd, &msg, 0);
|
||
|
if (res <= 0) {
|
||
|
printf("%s: sendmsg failed", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Listen on a unix domain socket named <socket_name>. */
|
||
|
/* Connect to it and return connect_fd */
|
||
|
int UnixSocketCreate(const char *socket_name) {
|
||
|
int listen_fd;
|
||
|
struct sockaddr_un sock_addr;
|
||
|
int connect_fd;
|
||
|
struct sockaddr_un connect_addr;
|
||
|
socklen_t connect_addr_len = 0;
|
||
|
|
||
|
listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
||
|
if (listen_fd < 0) {
|
||
|
printf("%s: socket create failed", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (verbose) printf("%s: listen_fd: %d\n", __func__, listen_fd);
|
||
|
|
||
|
unlink(socket_name);
|
||
|
|
||
|
memset(&sock_addr, 0, sizeof(struct sockaddr_un));
|
||
|
sock_addr.sun_family = AF_UNIX;
|
||
|
strncpy(sock_addr.sun_path, socket_name, sizeof(sock_addr.sun_path) - 1);
|
||
|
|
||
|
if (bind(listen_fd, (const struct sockaddr *)&sock_addr,
|
||
|
sizeof(struct sockaddr_un))) {
|
||
|
printf("i%s: bind error", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (listen(listen_fd, 1)) {
|
||
|
printf("%s: listen error", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
connect_fd =
|
||
|
accept(listen_fd, (struct sockaddr *)&connect_addr, &connect_addr_len);
|
||
|
|
||
|
if (verbose) printf("%s: connect_fd: %d\n", __func__, connect_fd);
|
||
|
|
||
|
close(listen_fd);
|
||
|
unlink(socket_name);
|
||
|
if (connect_fd < 0) {
|
||
|
printf("%s: accept failed\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return connect_fd;
|
||
|
}
|
||
|
|
||
|
/* receive a file descriptor from another process. */
|
||
|
/* Returns the file descriptor. Note: the integer value */
|
||
|
/* of the file descriptor may be different from the */
|
||
|
/* integer value in the other process, but the file */
|
||
|
/* descriptors in each process will refer to the same file */
|
||
|
/* object in the kernel. */
|
||
|
int EGLStreamReceivefd(int connect_fd) {
|
||
|
struct msghdr msg;
|
||
|
struct iovec iov[1];
|
||
|
char msg_buf[1];
|
||
|
char ctrl_buf[CMSG_SPACE(sizeof(int))];
|
||
|
struct cmsghdr *cmsg;
|
||
|
void *data;
|
||
|
int recvfd;
|
||
|
|
||
|
memset(&msg, 0, sizeof(msg));
|
||
|
|
||
|
iov[0].iov_base = msg_buf;
|
||
|
iov[0].iov_len = sizeof(msg_buf);
|
||
|
msg.msg_iov = iov;
|
||
|
msg.msg_iovlen = 1;
|
||
|
|
||
|
msg.msg_control = ctrl_buf;
|
||
|
msg.msg_controllen = sizeof(ctrl_buf);
|
||
|
|
||
|
if (recvmsg(connect_fd, &msg, 0) <= 0) {
|
||
|
printf("%s: recvmsg failed", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
cmsg = CMSG_FIRSTHDR(&msg);
|
||
|
if (!cmsg) {
|
||
|
printf("%s: NULL message header\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
if (cmsg->cmsg_level != SOL_SOCKET) {
|
||
|
printf("%s: Message level is not SOL_SOCKET\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||
|
printf("%s: Message type is not SCM_RIGHTS\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
data = CMSG_DATA(cmsg);
|
||
|
recvfd = *(int *)data;
|
||
|
|
||
|
return recvfd;
|
||
|
}
|
||
|
|
||
|
#endif
|