-
Nvidia Driver 375.39 on ubuntu is broken when it comes to EGL. Follow comment #11 to fix the host machine.
- To fix an nvidia-docker driver mount, you need to either fix
/var/lib/nvidia-docker/volumes/nvidia_driver/375.39/lib64
or use a docker viadocker run -it --rm -v nvidia_driver_375.39:/nvidia_driver debian
to gain write permissions to the drivers, and patch them.- Copy
/usr/lib/nvidia-375/libEGL.so.375.39
into the{nvidia_driver}/lib64/
directory - Make sure the symlinks for
{nvidia_driver}/lib64/libEGL.so.1
and{nvidia_driver}/lib64/libEGL.so
eventually link to thelibEGL.so.375.39
file.
- Copy
- To fix an nvidia-docker driver mount, you need to either fix
-
If you have the environment variable
DISPLAY
set, EGL will try and use the actual Display for offscreen OpenGL rendering instead of the headless route. This is difficult (but not impossible) because now you must run the command as the same user as the graphically logged in user. Plus this means you are using theDISPLAY
graphics card, not one of the others for rendering. This should be avoided. Eitherunset DISPLAY
in the shell or in C:unsetenv("DISPLAY"); //Force Headless eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
I have to call
glewInit();
before making any GLEW calls
- libgl1-mesa-dev
- libegl1-mesa-dev
- libglew-dev (optional if you use glew, this example does for FBO)
- libglew (optional if you use glew, this example does)
- Vendor OpenGL and EGL
- libglew
To get a specific headless display (called a platform), EGL extensions are needed. I would rather fail back to using the default eglGetDisplay
command if anything goes wrong.
EGLDisplay eglGetDisplay_(NativeDisplayType nativeDisplay=EGL_DEFAULT_DISPLAY)
{
EGLDisplay eglDisplay = eglGetDisplay(nativeDisplay);
checkEglError("Failed to Get Display: eglGetDisplay");
std::cerr << "Failback to eglGetDisplay" << std::endl;
return eglDisplay;
}
To redundantly getting a display platform:
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT");
checkEglError("Failed to get EGLEXT: eglQueryDevicesEXT");
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
checkEglError("Failed to get EGLEXT: eglGetPlatformDisplayEXT");
PFNEGLQUERYDEVICEATTRIBEXTPROC eglQueryDeviceAttribEXT = (PFNEGLQUERYDEVICEATTRIBEXTPROC)eglGetProcAddress("eglQueryDeviceAttribEXT");
checkEglError("Failed to get EGLEXT: eglQueryDeviceAttribEXT");
if (cudaIndexDesired >= 0)
{
EGLDeviceEXT *eglDevs;
EGLint numberDevices;
//Get number of devices
checkEglReturn(
eglQueryDevicesEXT(0, NULL, &numberDevices),
"Failed to get number of devices. Bad parameter suspected"
);
checkEglError("Error getting number of devices: eglQueryDevicesEXT");
std::cerr << numberDevices << " devices found" << std::endl;
if (numberDevices)
{
EGLAttrib cudaIndex;
//Get devices
eglDevs = new EGLDeviceEXT[numberDevices];
checkEglReturn(
eglQueryDevicesEXT(numberDevices, eglDevs, &numberDevices),
"Failed to get devices. Bad parameter suspected"
);
checkEglError("Error getting number of devices: eglQueryDevicesEXT");
for(i=0; i<numberDevices; i++)
{
checkEglReturn(
eglQueryDeviceAttribEXT(eglDevs[i], EGL_CUDA_DEVICE_NV, &cudaIndex),
"Failed to get EGL_CUDA_DEVICE_NV attribute for device"
);
checkEglError("Error retreiving EGL_CUDA_DEVICE_NV attribute for device");
if (cudaIndex == cudaIndexDesired)
break;
}
if (i < numberDevices)
{
eglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, eglDevs[i], 0);
checkEglError("Error getting Platform Display: eglGetPlatformDisplayEXT");
std::cerr << "Got Cuda device " << cudaIndex << std::endl;
}
else
{
eglDisplay = eglGetDisplay_();
}
}
else
{//If no devices were found, or a matching cuda not found, get a Display the normal way
eglDisplay = eglGetDisplay_();
}
}
else
{
eglDisplay = eglGetDisplay_();
}
if (eglDisplay == EGL_NO_DISPLAY)
throw EGLException("No Disply Found");
//printf("Display %lu used\n",*(uint64_t*)eglDisplay); Points, always says 0
eglInitialize(eglDisplay, &major, &minor);
checkEglError("Failed to initial display: eglInitialize");
std::cerr << "Display initialized for EGL " << major << "." << minor << std::endl;
There are usually many connections that match a given filter. EGL tries to sort them in a decent order. In my tests this worked out, but sometimes it is not ideal and will waste resources.
EGLint numberConfigs;
EGLConfig eglConfig;
eglChooseConfig(eglDisplay, configAttribs, &eglConfig, 1, &numberConfigs);
You have to bind one of the available APIs to your thread. Your choices are OpenGL, OpenGL ES, or OpenVG
eglBindAPI(EGL_OPENGL_API);
checkEglError("Failed to bind OpenGL API: eglBindAPI");
Now you are ready to create your context
EGLContext eglCtx = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, NULL);
checkEglError("Failed to create context: eglCreateContext");
Now when you are ready to draw/read/use your context, make it current every time
eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx);
checkEglError("Failed to make context current: eglMakeCurrent");
Since we usually want to use FBOs, there's no point in sending a dummy surface
Well, from here on, do what ever normal GL commands you usually do. Such as creating an FBO
GLuint color_tex, depth_tex;
glGenTextures(1, &color_tex);
glBindTexture(GL_TEXTURE_2D, color_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//NULL means reserve texture memory, but texels are undefined
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 300, 300, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glGenTextures(1, &depth_tex);
glBindTexture(GL_TEXTURE_2D, depth_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
//NULL means reserve texture memory, but texels are undefined
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 300, 300, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
GLuint fb;
glewInit();
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
//Attach 2D texture to this FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
And set up a viewpoint, and clear the buffer
glViewport(0, 0, (GLint) winWidth, (GLint) winHeight);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
And for a sanity check, read the buffer and write it to a raw text file
char* my_buffer = (char*)malloc(winWidth*winHeight*3);
FILE* my_file;
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0,0,winWidth, winHeight, GL_RGB, GL_UNSIGNED_BYTE, my_buffer);
my_file = fopen("test.bin", "wb");
fwrite(my_buffer, 3, winWidth*winHeight, my_file);
fclose(my_file);
free(my_buffer);
And read it in with some easy to use python (notebook)
%matplotlib notebook
from PIL import Image
import numpy as np
data = np.fromfile('test.bin', dtype=np.uint8)
data = data.reshape(300, 300, 3)
data = np.flipud(data)
img = Image.fromarray(data)
img
The error handling functions used above are:
#include <exception>
class EGLException:public std::exception
{
public:
const char* message;
EGLException(const char* mmessage): message(mmessage) {}
virtual const char* what() const throw()
{
return this->message;
}
};
class EGLReturnException: private EGLException
{
using EGLException::EGLException;
};
class EGLErrorException: private EGLException
{
using EGLException::EGLException;
};
#define checkEglError(message){ \
EGLint err = eglGetError(); \
if (err != EGL_SUCCESS) \
{ \
std::cerr << "EGL Error " << std::hex << err << std::dec << " on line " << __LINE__ << std::endl; \
throw EGLErrorException(message); \
} \
}
#define checkEglReturn(x, message){ \
if (x != EGL_TRUE) \
{ \
std::cerr << "EGL returned not true on line " << __LINE__ << std::endl; \
throw EGLReturnException(message); \
} \
}
Why do you install:
libgl1-mesa-dev
andlibegl1-mesa-dev
? I thought if you wanted nvidia acceleration you should avoid using mesa stuff?