rfrossard's picture
Duplicate from F4RF4R4/Image-and-3D-Model-Creator
1b0ce39
raw
history blame
5.05 kB
"""Headless GPU-accelerated OpenGL context creation on Google Colaboratory.
Typical usage:
# Optional PyOpenGL configuratiopn can be done here.
# import OpenGL
# OpenGL.ERROR_CHECKING = True
# 'glcontext' must be imported before any OpenGL.* API.
from lucid.misc.gl.glcontext import create_opengl_context
# Now it's safe to import OpenGL and EGL functions
import OpenGL.GL as gl
# create_opengl_context() creates a GL context that is attached to an
# offscreen surface of the specified size. Note that rendering to buffers
# of other sizes and formats is still possible with OpenGL Framebuffers.
#
# Users are expected to directly use the EGL API in case more advanced
# context management is required.
width, height = 640, 480
create_opengl_context((width, height))
# OpenGL context is available here.
"""
from __future__ import print_function
# pylint: disable=unused-import,g-import-not-at-top,g-statement-before-imports
try:
import OpenGL
except:
print('This module depends on PyOpenGL.')
print('Please run "\033[1m!pip install -q pyopengl\033[0m" '
'prior importing this module.')
raise
import ctypes
from ctypes import pointer, util
import os
os.environ['PYOPENGL_PLATFORM'] = 'egl'
# OpenGL loading workaround.
#
# * PyOpenGL tries to load libGL, but we need libOpenGL, see [1,2].
# This could have been solved by a symlink libGL->libOpenGL, but:
#
# * Python 2.7 can't find libGL and linEGL due to a bug (see [3])
# in ctypes.util, that was only wixed in Python 3.6.
#
# So, the only solution I've found is to monkeypatch ctypes.util
# [1] https://devblogs.nvidia.com/egl-eye-opengl-visualization-without-x-server/
# [2] https://devblogs.nvidia.com/linking-opengl-server-side-rendering/
# [3] https://bugs.python.org/issue9998
_find_library_old = ctypes.util.find_library
try:
def _find_library_new(name):
return {
'GL': 'libOpenGL.so',
'EGL': 'libEGL.so',
}.get(name, _find_library_old(name))
util.find_library = _find_library_new
import OpenGL.GL as gl
import OpenGL.EGL as egl
from OpenGL import error
from OpenGL.EGL.EXT.device_base import egl_get_devices
from OpenGL.raw.EGL.EXT.platform_device import EGL_PLATFORM_DEVICE_EXT
except:
print('Unable to load OpenGL libraries. '
'Make sure you use GPU-enabled backend.')
print('Press "Runtime->Change runtime type" and set '
'"Hardware accelerator" to GPU.')
raise
finally:
util.find_library = _find_library_old
def create_initialized_headless_egl_display():
"""Creates an initialized EGL display directly on a device."""
for device in egl_get_devices():
display = egl.eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, None)
if display != egl.EGL_NO_DISPLAY and egl.eglGetError() == egl.EGL_SUCCESS:
# `eglInitialize` may or may not raise an exception on failure depending
# on how PyOpenGL is configured. We therefore catch a `GLError` and also
# manually check the output of `eglGetError()` here.
try:
initialized = egl.eglInitialize(display, None, None)
except error.GLError:
pass
else:
if initialized == egl.EGL_TRUE and egl.eglGetError() == egl.EGL_SUCCESS:
return display
return egl.EGL_NO_DISPLAY
def create_opengl_context(surface_size=(640, 480)):
"""Create offscreen OpenGL context and make it current.
Users are expected to directly use EGL API in case more advanced
context management is required.
Args:
surface_size: (width, height), size of the offscreen rendering surface.
"""
egl_display = create_initialized_headless_egl_display()
if egl_display == egl.EGL_NO_DISPLAY:
raise ImportError('Cannot initialize a headless EGL display.')
major, minor = egl.EGLint(), egl.EGLint()
egl.eglInitialize(egl_display, pointer(major), pointer(minor))
config_attribs = [
egl.EGL_SURFACE_TYPE, egl.EGL_PBUFFER_BIT, egl.EGL_BLUE_SIZE, 8,
egl.EGL_GREEN_SIZE, 8, egl.EGL_RED_SIZE, 8, egl.EGL_DEPTH_SIZE, 24,
egl.EGL_RENDERABLE_TYPE, egl.EGL_OPENGL_BIT, egl.EGL_NONE
]
config_attribs = (egl.EGLint * len(config_attribs))(*config_attribs)
num_configs = egl.EGLint()
egl_cfg = egl.EGLConfig()
egl.eglChooseConfig(egl_display, config_attribs, pointer(egl_cfg), 1,
pointer(num_configs))
width, height = surface_size
pbuffer_attribs = [
egl.EGL_WIDTH,
width,
egl.EGL_HEIGHT,
height,
egl.EGL_NONE,
]
pbuffer_attribs = (egl.EGLint * len(pbuffer_attribs))(*pbuffer_attribs)
egl_surf = egl.eglCreatePbufferSurface(egl_display, egl_cfg, pbuffer_attribs)
egl.eglBindAPI(egl.EGL_OPENGL_API)
egl_context = egl.eglCreateContext(egl_display, egl_cfg, egl.EGL_NO_CONTEXT,
None)
egl.eglMakeCurrent(egl_display, egl_surf, egl_surf, egl_context)