pyrender-maintained 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyrender/offscreen.py ADDED
@@ -0,0 +1,160 @@
1
+ """Wrapper for offscreen rendering.
2
+
3
+ Author: Matthew Matl
4
+ """
5
+ import os
6
+
7
+ from .renderer import Renderer
8
+ from .constants import RenderFlags
9
+
10
+
11
+ class OffscreenRenderer(object):
12
+ """A wrapper for offscreen rendering.
13
+
14
+ Parameters
15
+ ----------
16
+ viewport_width : int
17
+ The width of the main viewport, in pixels.
18
+ viewport_height : int
19
+ The height of the main viewport, in pixels.
20
+ point_size : float
21
+ The size of screen-space points in pixels.
22
+ """
23
+
24
+ def __init__(self, viewport_width, viewport_height, point_size=1.0):
25
+ self.viewport_width = viewport_width
26
+ self.viewport_height = viewport_height
27
+ self.point_size = point_size
28
+
29
+ self._platform = None
30
+ self._renderer = None
31
+ self._create()
32
+
33
+ @property
34
+ def viewport_width(self):
35
+ """int : The width of the main viewport, in pixels.
36
+ """
37
+ return self._viewport_width
38
+
39
+ @viewport_width.setter
40
+ def viewport_width(self, value):
41
+ self._viewport_width = int(value)
42
+
43
+ @property
44
+ def viewport_height(self):
45
+ """int : The height of the main viewport, in pixels.
46
+ """
47
+ return self._viewport_height
48
+
49
+ @viewport_height.setter
50
+ def viewport_height(self, value):
51
+ self._viewport_height = int(value)
52
+
53
+ @property
54
+ def point_size(self):
55
+ """float : The pixel size of points in point clouds.
56
+ """
57
+ return self._point_size
58
+
59
+ @point_size.setter
60
+ def point_size(self, value):
61
+ self._point_size = float(value)
62
+
63
+ def render(self, scene, flags=RenderFlags.NONE, seg_node_map=None):
64
+ """Render a scene with the given set of flags.
65
+
66
+ Parameters
67
+ ----------
68
+ scene : :class:`Scene`
69
+ A scene to render.
70
+ flags : int
71
+ A bitwise or of one or more flags from :class:`.RenderFlags`.
72
+ seg_node_map : dict
73
+ A map from :class:`.Node` objects to (3,) colors for each.
74
+ If specified along with flags set to :attr:`.RenderFlags.SEG`,
75
+ the color image will be a segmentation image.
76
+
77
+ Returns
78
+ -------
79
+ color_im : (h, w, 3) uint8 or (h, w, 4) uint8
80
+ The color buffer in RGB format, or in RGBA format if
81
+ :attr:`.RenderFlags.RGBA` is set.
82
+ Not returned if flags includes :attr:`.RenderFlags.DEPTH_ONLY`.
83
+ depth_im : (h, w) float32
84
+ The depth buffer in linear units.
85
+ """
86
+ self._platform.make_current()
87
+ # If platform does not support dynamically-resizing framebuffers,
88
+ # destroy it and restart it
89
+ if (self._platform.viewport_height != self.viewport_height or
90
+ self._platform.viewport_width != self.viewport_width):
91
+ if not self._platform.supports_framebuffers():
92
+ self.delete()
93
+ self._create()
94
+
95
+ self._platform.make_current()
96
+ self._renderer.viewport_width = self.viewport_width
97
+ self._renderer.viewport_height = self.viewport_height
98
+ self._renderer.point_size = self.point_size
99
+
100
+ if self._platform.supports_framebuffers():
101
+ flags |= RenderFlags.OFFSCREEN
102
+ retval = self._renderer.render(scene, flags, seg_node_map)
103
+ else:
104
+ self._renderer.render(scene, flags, seg_node_map)
105
+ depth = self._renderer.read_depth_buf()
106
+ if flags & RenderFlags.DEPTH_ONLY:
107
+ retval = depth
108
+ else:
109
+ color = self._renderer.read_color_buf()
110
+ retval = color, depth
111
+
112
+ # Make the platform not current
113
+ self._platform.make_uncurrent()
114
+ return retval
115
+
116
+ def delete(self):
117
+ """Free all OpenGL resources.
118
+ """
119
+ self._platform.make_current()
120
+ self._renderer.delete()
121
+ self._platform.delete_context()
122
+ del self._renderer
123
+ del self._platform
124
+ self._renderer = None
125
+ self._platform = None
126
+ import gc
127
+ gc.collect()
128
+
129
+ def _create(self):
130
+ if 'PYOPENGL_PLATFORM' not in os.environ:
131
+ from pyrender.platforms.pyglet_platform import PygletPlatform
132
+ self._platform = PygletPlatform(self.viewport_width,
133
+ self.viewport_height)
134
+ elif os.environ['PYOPENGL_PLATFORM'] == 'egl':
135
+ from pyrender.platforms import egl
136
+ device_id = int(os.environ.get('EGL_DEVICE_ID', '0'))
137
+ egl_device = egl.get_device_by_index(device_id)
138
+ self._platform = egl.EGLPlatform(self.viewport_width,
139
+ self.viewport_height,
140
+ device=egl_device)
141
+ elif os.environ['PYOPENGL_PLATFORM'] == 'osmesa':
142
+ from pyrender.platforms.osmesa import OSMesaPlatform
143
+ self._platform = OSMesaPlatform(self.viewport_width,
144
+ self.viewport_height)
145
+ else:
146
+ raise ValueError('Unsupported PyOpenGL platform: {}'.format(
147
+ os.environ['PYOPENGL_PLATFORM']
148
+ ))
149
+ self._platform.init_context()
150
+ self._platform.make_current()
151
+ self._renderer = Renderer(self.viewport_width, self.viewport_height)
152
+
153
+ def __del__(self):
154
+ try:
155
+ self.delete()
156
+ except Exception:
157
+ pass
158
+
159
+
160
+ __all__ = ['OffscreenRenderer']
@@ -0,0 +1,6 @@
1
+ """Platforms for generating offscreen OpenGL contexts for rendering.
2
+
3
+ Author: Matthew Matl
4
+ """
5
+
6
+ from .base import Platform
@@ -0,0 +1,73 @@
1
+ import abc
2
+
3
+
4
+ class Platform(abc.ABC):
5
+ """Base class for all OpenGL platforms.
6
+
7
+ Parameters
8
+ ----------
9
+ viewport_width : int
10
+ The width of the main viewport, in pixels.
11
+ viewport_height : int
12
+ The height of the main viewport, in pixels
13
+ """
14
+
15
+ def __init__(self, viewport_width, viewport_height):
16
+ self.viewport_width = viewport_width
17
+ self.viewport_height = viewport_height
18
+
19
+ @property
20
+ def viewport_width(self):
21
+ """int : The width of the main viewport, in pixels.
22
+ """
23
+ return self._viewport_width
24
+
25
+ @viewport_width.setter
26
+ def viewport_width(self, value):
27
+ self._viewport_width = value
28
+
29
+ @property
30
+ def viewport_height(self):
31
+ """int : The height of the main viewport, in pixels.
32
+ """
33
+ return self._viewport_height
34
+
35
+ @viewport_height.setter
36
+ def viewport_height(self, value):
37
+ self._viewport_height = value
38
+
39
+ @abc.abstractmethod
40
+ def init_context(self):
41
+ """Create an OpenGL context.
42
+ """
43
+ pass
44
+
45
+ @abc.abstractmethod
46
+ def make_current(self):
47
+ """Make the OpenGL context current.
48
+ """
49
+ pass
50
+
51
+ @abc.abstractmethod
52
+ def make_uncurrent(self):
53
+ """Make the OpenGL context uncurrent.
54
+ """
55
+ pass
56
+
57
+ @abc.abstractmethod
58
+ def delete_context(self):
59
+ """Delete the OpenGL context.
60
+ """
61
+ pass
62
+
63
+ @abc.abstractmethod
64
+ def supports_framebuffers(self):
65
+ """Returns True if the method supports framebuffer rendering.
66
+ """
67
+ pass
68
+
69
+ def __del__(self):
70
+ try:
71
+ self.delete_context()
72
+ except Exception:
73
+ pass
@@ -0,0 +1,219 @@
1
+ import ctypes
2
+ import os
3
+
4
+ import OpenGL.platform
5
+
6
+ from .base import Platform
7
+
8
+ EGL_PLATFORM_DEVICE_EXT = 0x313F
9
+ EGL_DRM_DEVICE_FILE_EXT = 0x3233
10
+
11
+
12
+ def _ensure_egl_loaded():
13
+ plugin = OpenGL.platform.PlatformPlugin.by_name('egl')
14
+ if plugin is None:
15
+ raise RuntimeError("EGL platform plugin is not available.")
16
+
17
+ plugin_class = plugin.load()
18
+ plugin.loaded = True
19
+ # create instance of this platform implementation
20
+ plugin = plugin_class()
21
+
22
+ plugin.install(vars(OpenGL.platform))
23
+
24
+
25
+ _ensure_egl_loaded()
26
+ from OpenGL import EGL as egl
27
+
28
+
29
+ def _get_egl_func(func_name, res_type, *arg_types):
30
+ address = egl.eglGetProcAddress(func_name)
31
+ if address is None:
32
+ return None
33
+
34
+ proto = ctypes.CFUNCTYPE(res_type)
35
+ proto.argtypes = arg_types
36
+ func = proto(address)
37
+ return func
38
+
39
+
40
+ def _get_egl_struct(struct_name):
41
+ from OpenGL._opaque import opaque_pointer_cls
42
+ return opaque_pointer_cls(struct_name)
43
+
44
+
45
+ # These are not defined in PyOpenGL by default.
46
+ _EGLDeviceEXT = _get_egl_struct('EGLDeviceEXT')
47
+ _eglGetPlatformDisplayEXT = _get_egl_func('eglGetPlatformDisplayEXT', egl.EGLDisplay)
48
+ _eglQueryDevicesEXT = _get_egl_func('eglQueryDevicesEXT', egl.EGLBoolean)
49
+ _eglQueryDeviceStringEXT = _get_egl_func('eglQueryDeviceStringEXT', ctypes.c_char_p)
50
+
51
+
52
+ def query_devices():
53
+ if _eglQueryDevicesEXT is None:
54
+ raise RuntimeError("EGL query extension is not loaded or is not supported.")
55
+
56
+ num_devices = egl.EGLint()
57
+ success = _eglQueryDevicesEXT(0, None, ctypes.pointer(num_devices))
58
+ if not success or num_devices.value < 1:
59
+ return []
60
+
61
+ devices = (_EGLDeviceEXT * num_devices.value)() # array of size num_devices
62
+ success = _eglQueryDevicesEXT(num_devices.value, devices, ctypes.pointer(num_devices))
63
+ if not success or num_devices.value < 1:
64
+ return []
65
+
66
+ return [EGLDevice(devices[i]) for i in range(num_devices.value)]
67
+
68
+
69
+ def get_default_device():
70
+ # Fall back to not using query extension.
71
+ if _eglQueryDevicesEXT is None:
72
+ return EGLDevice(None)
73
+
74
+ return query_devices()[0]
75
+
76
+
77
+ def get_device_by_index(device_id):
78
+ if _eglQueryDevicesEXT is None and device_id == 0:
79
+ return get_default_device()
80
+
81
+ devices = query_devices()
82
+ if device_id >= len(devices):
83
+ raise ValueError('Invalid device ID ({})'.format(device_id, len(devices)))
84
+ return devices[device_id]
85
+
86
+
87
+ class EGLDevice:
88
+
89
+ def __init__(self, display=None):
90
+ self._display = display
91
+
92
+ def get_display(self):
93
+ if self._display is None:
94
+ return egl.eglGetDisplay(egl.EGL_DEFAULT_DISPLAY)
95
+
96
+ return _eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, self._display, None)
97
+
98
+ @property
99
+ def name(self):
100
+ if self._display is None:
101
+ return 'default'
102
+
103
+ name = _eglQueryDeviceStringEXT(self._display, EGL_DRM_DEVICE_FILE_EXT)
104
+ if name is None:
105
+ return None
106
+
107
+ return name.decode('ascii')
108
+
109
+ def __repr__(self):
110
+ return "<EGLDevice(name={})>".format(self.name)
111
+
112
+
113
+ class EGLPlatform(Platform):
114
+ """Renders using EGL.
115
+ """
116
+
117
+ def __init__(self, viewport_width, viewport_height, device: EGLDevice = None):
118
+ super(EGLPlatform, self).__init__(viewport_width, viewport_height)
119
+ if device is None:
120
+ device = get_default_device()
121
+
122
+ self._egl_device = device
123
+ self._egl_display = None
124
+ self._egl_context = None
125
+
126
+ def init_context(self):
127
+ _ensure_egl_loaded()
128
+
129
+ from OpenGL.EGL import (
130
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_BLUE_SIZE,
131
+ EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_DEPTH_SIZE,
132
+ EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
133
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_CONFORMANT,
134
+ EGL_NONE, EGL_DEFAULT_DISPLAY, EGL_NO_CONTEXT,
135
+ EGL_OPENGL_API, EGL_CONTEXT_MAJOR_VERSION,
136
+ EGL_CONTEXT_MINOR_VERSION,
137
+ EGL_CONTEXT_OPENGL_PROFILE_MASK,
138
+ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
139
+ eglGetDisplay, eglInitialize, eglChooseConfig,
140
+ eglBindAPI, eglCreateContext, EGLConfig
141
+ )
142
+ from OpenGL import arrays
143
+
144
+ config_attributes = arrays.GLintArray.asArray([
145
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
146
+ EGL_BLUE_SIZE, 8,
147
+ EGL_RED_SIZE, 8,
148
+ EGL_GREEN_SIZE, 8,
149
+ EGL_DEPTH_SIZE, 24,
150
+ EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
151
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
152
+ EGL_CONFORMANT, EGL_OPENGL_BIT,
153
+ EGL_NONE
154
+ ])
155
+ context_attributes = arrays.GLintArray.asArray([
156
+ EGL_CONTEXT_MAJOR_VERSION, 4,
157
+ EGL_CONTEXT_MINOR_VERSION, 1,
158
+ EGL_CONTEXT_OPENGL_PROFILE_MASK,
159
+ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
160
+ EGL_NONE
161
+ ])
162
+ major, minor = ctypes.c_long(), ctypes.c_long()
163
+ num_configs = ctypes.c_long()
164
+ configs = (EGLConfig * 1)()
165
+
166
+ # Cache DISPLAY if necessary and get an off-screen EGL display
167
+ orig_dpy = None
168
+ if 'DISPLAY' in os.environ:
169
+ orig_dpy = os.environ['DISPLAY']
170
+ del os.environ['DISPLAY']
171
+
172
+ self._egl_display = self._egl_device.get_display()
173
+ if orig_dpy is not None:
174
+ os.environ['DISPLAY'] = orig_dpy
175
+
176
+ # Initialize EGL
177
+ assert eglInitialize(self._egl_display, major, minor)
178
+ assert eglChooseConfig(
179
+ self._egl_display, config_attributes, configs, 1, num_configs
180
+ )
181
+
182
+ # Bind EGL to the OpenGL API
183
+ assert eglBindAPI(EGL_OPENGL_API)
184
+
185
+ # Create an EGL context
186
+ self._egl_context = eglCreateContext(
187
+ self._egl_display, configs[0],
188
+ EGL_NO_CONTEXT, context_attributes
189
+ )
190
+
191
+ # Make it current
192
+ self.make_current()
193
+
194
+ def make_current(self):
195
+ from OpenGL.EGL import eglMakeCurrent, EGL_NO_SURFACE
196
+ assert eglMakeCurrent(
197
+ self._egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
198
+ self._egl_context
199
+ )
200
+
201
+ def make_uncurrent(self):
202
+ """Make the OpenGL context uncurrent.
203
+ """
204
+ pass
205
+
206
+ def delete_context(self):
207
+ from OpenGL.EGL import eglDestroyContext, eglTerminate
208
+ if self._egl_display is not None:
209
+ if self._egl_context is not None:
210
+ eglDestroyContext(self._egl_display, self._egl_context)
211
+ self._egl_context = None
212
+ eglTerminate(self._egl_display)
213
+ self._egl_display = None
214
+
215
+ def supports_framebuffers(self):
216
+ return True
217
+
218
+
219
+ __all__ = ['EGLPlatform']
@@ -0,0 +1,59 @@
1
+ from .base import Platform
2
+
3
+
4
+ __all__ = ['OSMesaPlatform']
5
+
6
+
7
+ class OSMesaPlatform(Platform):
8
+ """Renders into a software buffer using OSMesa. Requires special versions
9
+ of OSMesa to be installed, plus PyOpenGL upgrade.
10
+ """
11
+
12
+ def __init__(self, viewport_width, viewport_height):
13
+ super(OSMesaPlatform, self).__init__(viewport_width, viewport_height)
14
+ self._context = None
15
+ self._buffer = None
16
+
17
+ def init_context(self):
18
+ from OpenGL import arrays
19
+ from OpenGL.osmesa import (
20
+ OSMesaCreateContextAttribs, OSMESA_FORMAT,
21
+ OSMESA_RGBA, OSMESA_PROFILE, OSMESA_CORE_PROFILE,
22
+ OSMESA_CONTEXT_MAJOR_VERSION, OSMESA_CONTEXT_MINOR_VERSION,
23
+ OSMESA_DEPTH_BITS
24
+ )
25
+
26
+ attrs = arrays.GLintArray.asArray([
27
+ OSMESA_FORMAT, OSMESA_RGBA,
28
+ OSMESA_DEPTH_BITS, 24,
29
+ OSMESA_PROFILE, OSMESA_CORE_PROFILE,
30
+ OSMESA_CONTEXT_MAJOR_VERSION, 3,
31
+ OSMESA_CONTEXT_MINOR_VERSION, 3,
32
+ 0
33
+ ])
34
+ self._context = OSMesaCreateContextAttribs(attrs, None)
35
+ self._buffer = arrays.GLubyteArray.zeros(
36
+ (self.viewport_height, self.viewport_width, 4)
37
+ )
38
+
39
+ def make_current(self):
40
+ from OpenGL import GL as gl
41
+ from OpenGL.osmesa import OSMesaMakeCurrent
42
+ assert(OSMesaMakeCurrent(
43
+ self._context, self._buffer, gl.GL_UNSIGNED_BYTE,
44
+ self.viewport_width, self.viewport_height
45
+ ))
46
+
47
+ def make_uncurrent(self):
48
+ """Make the OpenGL context uncurrent.
49
+ """
50
+ pass
51
+
52
+ def delete_context(self):
53
+ from OpenGL.osmesa import OSMesaDestroyContext
54
+ OSMesaDestroyContext(self._context)
55
+ self._context = None
56
+ self._buffer = None
57
+
58
+ def supports_framebuffers(self):
59
+ return False
@@ -0,0 +1,90 @@
1
+ from pyrender.constants import (TARGET_OPEN_GL_MAJOR, TARGET_OPEN_GL_MINOR,
2
+ MIN_OPEN_GL_MAJOR, MIN_OPEN_GL_MINOR)
3
+ from .base import Platform
4
+
5
+ import OpenGL
6
+
7
+
8
+ __all__ = ['PygletPlatform']
9
+
10
+
11
+ class PygletPlatform(Platform):
12
+ """Renders on-screen using a 1x1 hidden Pyglet window for getting
13
+ an OpenGL context.
14
+ """
15
+
16
+ def __init__(self, viewport_width, viewport_height):
17
+ super(PygletPlatform, self).__init__(viewport_width, viewport_height)
18
+ self._window = None
19
+
20
+ def init_context(self):
21
+ import pyglet
22
+ pyglet.options['shadow_window'] = False
23
+
24
+ try:
25
+ pyglet.lib.x11.xlib.XInitThreads()
26
+ except Exception:
27
+ pass
28
+
29
+ self._window = None
30
+ confs = [pyglet.gl.Config(sample_buffers=1, samples=4,
31
+ depth_size=24,
32
+ double_buffer=True,
33
+ major_version=TARGET_OPEN_GL_MAJOR,
34
+ minor_version=TARGET_OPEN_GL_MINOR),
35
+ pyglet.gl.Config(depth_size=24,
36
+ double_buffer=True,
37
+ major_version=TARGET_OPEN_GL_MAJOR,
38
+ minor_version=TARGET_OPEN_GL_MINOR),
39
+ pyglet.gl.Config(sample_buffers=1, samples=4,
40
+ depth_size=24,
41
+ double_buffer=True,
42
+ major_version=MIN_OPEN_GL_MAJOR,
43
+ minor_version=MIN_OPEN_GL_MINOR),
44
+ pyglet.gl.Config(depth_size=24,
45
+ double_buffer=True,
46
+ major_version=MIN_OPEN_GL_MAJOR,
47
+ minor_version=MIN_OPEN_GL_MINOR)]
48
+ for conf in confs:
49
+ try:
50
+ self._window = pyglet.window.Window(config=conf, visible=False,
51
+ resizable=False,
52
+ width=1, height=1)
53
+ break
54
+ except pyglet.window.NoSuchConfigException as e:
55
+ pass
56
+
57
+ if not self._window:
58
+ raise ValueError(
59
+ 'Failed to initialize Pyglet window with an OpenGL >= 3+ '
60
+ 'context. If you\'re logged in via SSH, ensure that you\'re '
61
+ 'running your script with vglrun (i.e. VirtualGL). The '
62
+ 'internal error message was "{}"'.format(e)
63
+ )
64
+
65
+ def make_current(self):
66
+ if self._window:
67
+ self._window.switch_to()
68
+
69
+ def make_uncurrent(self):
70
+ try:
71
+ import pyglet
72
+ pyglet.gl.xlib.glx.glXMakeContextCurrent(self._window.context.x_display, 0, 0, None)
73
+ except Exception:
74
+ pass
75
+
76
+ def delete_context(self):
77
+ if self._window is not None:
78
+ self.make_current()
79
+ cid = OpenGL.contextdata.getContext()
80
+ try:
81
+ self._window.context.destroy()
82
+ self._window.close()
83
+ except Exception:
84
+ pass
85
+ self._window = None
86
+ OpenGL.contextdata.cleanupContext(cid)
87
+ del cid
88
+
89
+ def supports_framebuffers(self):
90
+ return True