/pyrender/platforms/egl.py

https://github.com/mmatl/pyrender · Python · 219 lines · 152 code · 55 blank · 12 comment · 20 complexity · 3b9b81485b0c52f42366d43f9beee00a MD5 · raw file

  1. import ctypes
  2. import os
  3. import OpenGL.platform
  4. from .base import Platform
  5. EGL_PLATFORM_DEVICE_EXT = 0x313F
  6. EGL_DRM_DEVICE_FILE_EXT = 0x3233
  7. def _ensure_egl_loaded():
  8. plugin = OpenGL.platform.PlatformPlugin.by_name('egl')
  9. if plugin is None:
  10. raise RuntimeError("EGL platform plugin is not available.")
  11. plugin_class = plugin.load()
  12. plugin.loaded = True
  13. # create instance of this platform implementation
  14. plugin = plugin_class()
  15. plugin.install(vars(OpenGL.platform))
  16. _ensure_egl_loaded()
  17. from OpenGL import EGL as egl
  18. def _get_egl_func(func_name, res_type, *arg_types):
  19. address = egl.eglGetProcAddress(func_name)
  20. if address is None:
  21. return None
  22. proto = ctypes.CFUNCTYPE(res_type)
  23. proto.argtypes = arg_types
  24. func = proto(address)
  25. return func
  26. def _get_egl_struct(struct_name):
  27. from OpenGL._opaque import opaque_pointer_cls
  28. return opaque_pointer_cls(struct_name)
  29. # These are not defined in PyOpenGL by default.
  30. _EGLDeviceEXT = _get_egl_struct('EGLDeviceEXT')
  31. _eglGetPlatformDisplayEXT = _get_egl_func('eglGetPlatformDisplayEXT', egl.EGLDisplay)
  32. _eglQueryDevicesEXT = _get_egl_func('eglQueryDevicesEXT', egl.EGLBoolean)
  33. _eglQueryDeviceStringEXT = _get_egl_func('eglQueryDeviceStringEXT', ctypes.c_char_p)
  34. def query_devices():
  35. if _eglQueryDevicesEXT is None:
  36. raise RuntimeError("EGL query extension is not loaded or is not supported.")
  37. num_devices = egl.EGLint()
  38. success = _eglQueryDevicesEXT(0, None, ctypes.pointer(num_devices))
  39. if not success or num_devices.value < 1:
  40. return []
  41. devices = (_EGLDeviceEXT * num_devices.value)() # array of size num_devices
  42. success = _eglQueryDevicesEXT(num_devices.value, devices, ctypes.pointer(num_devices))
  43. if not success or num_devices.value < 1:
  44. return []
  45. return [EGLDevice(devices[i]) for i in range(num_devices.value)]
  46. def get_default_device():
  47. # Fall back to not using query extension.
  48. if _eglQueryDevicesEXT is None:
  49. return EGLDevice(None)
  50. return query_devices()[0]
  51. def get_device_by_index(device_id):
  52. if _eglQueryDevicesEXT is None and device_id == 0:
  53. return get_default_device()
  54. devices = query_devices()
  55. if device_id >= len(devices):
  56. raise ValueError('Invalid device ID ({})'.format(device_id, len(devices)))
  57. return devices[device_id]
  58. class EGLDevice:
  59. def __init__(self, display=None):
  60. self._display = display
  61. def get_display(self):
  62. if self._display is None:
  63. return egl.eglGetDisplay(egl.EGL_DEFAULT_DISPLAY)
  64. return _eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, self._display, None)
  65. @property
  66. def name(self):
  67. if self._display is None:
  68. return 'default'
  69. name = _eglQueryDeviceStringEXT(self._display, EGL_DRM_DEVICE_FILE_EXT)
  70. if name is None:
  71. return None
  72. return name.decode('ascii')
  73. def __repr__(self):
  74. return "<EGLDevice(name={})>".format(self.name)
  75. class EGLPlatform(Platform):
  76. """Renders using EGL (not currently working on Ubuntu).
  77. """
  78. def __init__(self, viewport_width, viewport_height, device: EGLDevice = None):
  79. super(EGLPlatform, self).__init__(viewport_width, viewport_height)
  80. if device is None:
  81. device = get_default_device()
  82. self._egl_device = device
  83. self._egl_display = None
  84. self._egl_context = None
  85. def init_context(self):
  86. _ensure_egl_loaded()
  87. from OpenGL.EGL import (
  88. EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_BLUE_SIZE,
  89. EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_DEPTH_SIZE,
  90. EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
  91. EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_CONFORMANT,
  92. EGL_NONE, EGL_DEFAULT_DISPLAY, EGL_NO_CONTEXT,
  93. EGL_OPENGL_API, EGL_CONTEXT_MAJOR_VERSION,
  94. EGL_CONTEXT_MINOR_VERSION,
  95. EGL_CONTEXT_OPENGL_PROFILE_MASK,
  96. EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
  97. eglGetDisplay, eglInitialize, eglChooseConfig,
  98. eglBindAPI, eglCreateContext, EGLConfig
  99. )
  100. from OpenGL import arrays
  101. config_attributes = arrays.GLintArray.asArray([
  102. EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
  103. EGL_BLUE_SIZE, 8,
  104. EGL_RED_SIZE, 8,
  105. EGL_GREEN_SIZE, 8,
  106. EGL_DEPTH_SIZE, 24,
  107. EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
  108. EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
  109. EGL_CONFORMANT, EGL_OPENGL_BIT,
  110. EGL_NONE
  111. ])
  112. context_attributes = arrays.GLintArray.asArray([
  113. EGL_CONTEXT_MAJOR_VERSION, 4,
  114. EGL_CONTEXT_MINOR_VERSION, 1,
  115. EGL_CONTEXT_OPENGL_PROFILE_MASK,
  116. EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
  117. EGL_NONE
  118. ])
  119. major, minor = ctypes.c_long(), ctypes.c_long()
  120. num_configs = ctypes.c_long()
  121. configs = (EGLConfig * 1)()
  122. # Cache DISPLAY if necessary and get an off-screen EGL display
  123. orig_dpy = None
  124. if 'DISPLAY' in os.environ:
  125. orig_dpy = os.environ['DISPLAY']
  126. del os.environ['DISPLAY']
  127. self._egl_display = self._egl_device.get_display()
  128. if orig_dpy is not None:
  129. os.environ['DISPLAY'] = orig_dpy
  130. # Initialize EGL
  131. assert eglInitialize(self._egl_display, major, minor)
  132. assert eglChooseConfig(
  133. self._egl_display, config_attributes, configs, 1, num_configs
  134. )
  135. # Bind EGL to the OpenGL API
  136. assert eglBindAPI(EGL_OPENGL_API)
  137. # Create an EGL context
  138. self._egl_context = eglCreateContext(
  139. self._egl_display, configs[0],
  140. EGL_NO_CONTEXT, context_attributes
  141. )
  142. # Make it current
  143. self.make_current()
  144. def make_current(self):
  145. from OpenGL.EGL import eglMakeCurrent, EGL_NO_SURFACE
  146. assert eglMakeCurrent(
  147. self._egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
  148. self._egl_context
  149. )
  150. def make_uncurrent(self):
  151. """Make the OpenGL context uncurrent.
  152. """
  153. pass
  154. def delete_context(self):
  155. from OpenGL.EGL import eglDestroyContext, eglTerminate
  156. if self._egl_display is not None:
  157. if self._egl_context is not None:
  158. eglDestroyContext(self._egl_display, self._egl_context)
  159. self._egl_context = None
  160. eglTerminate(self._egl_display)
  161. self._egl_display = None
  162. def supports_framebuffers(self):
  163. return True
  164. __all__ = ['EGLPlatform']