mujoco_py/gl/eglshim.c (214 lines of code) (raw):

#define EGL_EGLEXT_PROTOTYPES #include "egl.h" #include "eglext.h" #include <GL/glew.h> #include "mujoco.h" #include "mjrender.h" #include "glshim.h" #define MAX_DEVICES 8 int is_device_initialized[MAX_DEVICES] = {0}; EGLDisplay eglDisplays[MAX_DEVICES]; EGLContext eglContexts[MAX_DEVICES]; int usingEGL() { return 1; } int initOpenGL(int device_id) { if (device_id < 0 || device_id > MAX_DEVICES) { printf("Device id outside of range.\n"); return -1; } int is_initialized = is_device_initialized[device_id]; if (is_initialized) return 1; // desired config const EGLint configAttribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE }; EGLDeviceEXT eglDevs[MAX_DEVICES]; EGLint numDevices; PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC) eglGetProcAddress("eglQueryDevicesEXT"); eglQueryDevicesEXT(MAX_DEVICES, eglDevs, &numDevices); printf("Found %d GPUs for rendering. Using device %d.\n", numDevices, device_id); if (device_id >= numDevices) { printf("Device id outside of range of available devices.\n"); return -1; } PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress("eglGetPlatformDisplayEXT"); if (eglGetPlatformDisplayEXT == NULL) { printf("Failed to get eglGetPlatformDisplayEXT\n"); return -2; } EGLDisplay eglDpy = eglGetPlatformDisplayEXT( EGL_PLATFORM_DEVICE_EXT, eglDevs[device_id], 0); // get default display // EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (eglDpy == EGL_NO_DISPLAY) { printf("Could not get EGL display\n"); return -3; } // initialize EGLint major, minor; if (eglInitialize(eglDpy, &major, &minor) != EGL_TRUE) { printf("Could not initialize EGL\n"); return -4; } // choose config EGLint numConfigs; EGLConfig eglCfg; if (eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs)!=EGL_TRUE ) { printf("Could not choose EGL config\n"); return -5; } // bind OpenGL API if( eglBindAPI(EGL_OPENGL_API)!=EGL_TRUE ) { printf("Could not bind EGL OpenGL API\n"); return -6; } // create context EGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL); if( eglCtx==EGL_NO_CONTEXT ) { printf("Could not create EGL context\n"); return -7; } // make context current, no surface (let OpenGL handle FBO) if( eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx)!=EGL_TRUE ) { eglDestroyContext(eglDpy, eglCtx); printf("Could not make EGL context current\n"); return -8; } GLenum err = glewInit(); if( GLEW_OK != err ) { // MuJoCo does this automatically, but we need it if we want // to create e.g. PBOs before calling MuJoCo rendering functions. fprintf("glewInit error: %s\n", glewGetErrorString(err)); return -9; } is_device_initialized[device_id] = 1; eglDisplays[device_id] = eglDpy; eglContexts[device_id] = eglCtx; return 1; } int makeOpenGLContextCurrent(int device_id) { if (device_id < 0 || device_id > MAX_DEVICES) { printf("Device id outside of range.\n"); return -1; } if (!is_device_initialized[device_id]) return -2; if( eglMakeCurrent(eglDisplays[device_id], EGL_NO_SURFACE, EGL_NO_SURFACE, eglContexts[device_id]) != EGL_TRUE ) { eglDestroyContext(eglDisplays[device_id], eglContexts[device_id]); printf("Could not make EGL context current\n"); return -3; } else { return 1; } } int setOpenGLBufferSize(int device_id, int width, int height) { // Noop since we don't need to change buffer here. return 1; } void closeOpenGL() { int device_id; for (device_id=0; device_id<MAX_DEVICES; device_id++) { if (!is_device_initialized[device_id]) continue; EGLDisplay eglDpy = eglDisplays[device_id]; if( eglDpy==EGL_NO_DISPLAY ) continue; // get current context EGLContext eglCtx = eglContexts[device_id]; // release context eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); // destroy context if valid if( eglCtx!=EGL_NO_CONTEXT ) eglDestroyContext(eglDpy, eglCtx); // terminate display eglTerminate(eglDpy); } } // Create Pixel Buffer Objects (PBO) for rgb and depth image, which can be // mapped to the CPU directly as a big batch. unsigned int createPBO(int width, int height, int batchSize, int use_short) { GLuint pixelBuffer = 0; glGenBuffers(1, &pixelBuffer); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pixelBuffer); GLsizeiptr buffer_size; if (use_short) { buffer_size = batchSize * width * height * sizeof(short); } else { buffer_size = batchSize * width * height * 3; } glBufferData(GL_PIXEL_PACK_BUFFER_ARB, buffer_size, 0, GL_DYNAMIC_READ); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); return (unsigned int) pixelBuffer; } void freePBO(unsigned int pixelBuffer) { glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); glDeleteBuffers(1, &pixelBuffer); } void readPBO(unsigned char *buffer_rgb, unsigned short *buffer_depth, unsigned int pbo_rgb, unsigned int pbo_depth, int width, int height, int batchSize) { if (pbo_rgb > 0) { glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo_rgb); GLubyte* src_rgb = (GLubyte*) glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); memcpy(buffer_rgb, src_rgb, batchSize * width * height * 3); glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); } if (pbo_depth > 0) { glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo_depth); GLushort* src_depth = (GLushort*) glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); memcpy(buffer_depth, src_depth, batchSize * width * height * sizeof(GLushort)); glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); } glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); } // Copy the render data from the Framebuffer Object (FBO) that Mujoco // uses for offscreen rendering into the PBOs. void copyFBOToPBO(mjrContext* con, unsigned int pbo_rgb, unsigned int pbo_depth, mjrRect viewport, int bufferOffset) { GLbitfield mask = (pbo_rgb ? GL_COLOR_BUFFER_BIT : 0) | (pbo_depth ? GL_DEPTH_BUFFER_BIT : 0); if (!mask) return; // multisample: blit to resolve buffer and read from there if (con->offSamples) { // make sure blit is supported if( !glBlitFramebuffer ) return; // prepare for resolve-blit glBindFramebuffer(GL_READ_FRAMEBUFFER, con->offFBO); glReadBuffer(GL_COLOR_ATTACHMENT0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, con->offFBO_r); glDrawBuffer(GL_COLOR_ATTACHMENT0); // resolve-blit glBlitFramebuffer(viewport.left, viewport.bottom, viewport.left + viewport.width, viewport.bottom + viewport.height, viewport.left, viewport.bottom, viewport.left + viewport.width, viewport.bottom + viewport.height, mask, GL_NEAREST); // read from resolved glBindFramebuffer(GL_READ_FRAMEBUFFER, con->offFBO_r); } // no multisample: read from offscreen else glBindFramebuffer(GL_READ_FRAMEBUFFER, con->offFBO); glReadBuffer(GL_COLOR_ATTACHMENT0); if (pbo_rgb) { glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo_rgb); glReadPixels(viewport.left, viewport.bottom, viewport.width, viewport.height, GL_RGB, GL_UNSIGNED_BYTE, bufferOffset * viewport.width * viewport.height * 3); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); } if (pbo_depth) { glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo_depth); glReadPixels(viewport.left, viewport.bottom, viewport.width, viewport.height, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, bufferOffset * viewport.width * viewport.height * sizeof(short)); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); } // restore currentBuffer glBindFramebuffer(GL_FRAMEBUFFER, con->offFBO); glReadBuffer(GL_COLOR_ATTACHMENT0); glDrawBuffer(GL_COLOR_ATTACHMENT0); }