1# ---------------------------------------------------------------------------- 2# pyglet 3# Copyright (c) 2006-2008 Alex Holkner 4# Copyright (c) 2008-2021 pyglet contributors 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 11# * Redistributions of source code must retain the above copyright 12# notice, this list of conditions and the following disclaimer. 13# * Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in 15# the documentation and/or other materials provided with the 16# distribution. 17# * Neither the name of pyglet nor the names of its 18# contributors may be used to endorse or promote products 19# derived from this software without specific prior written 20# permission. 21# 22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33# POSSIBILITY OF SUCH DAMAGE. 34# ---------------------------------------------------------------------------- 35 36import warnings 37from ctypes import * 38 39from .base import Config, CanvasConfig, Context 40from pyglet.canvas.headless import HeadlessCanvas 41from pyglet.libs.egl import egl 42from pyglet.libs.egl.egl import * 43from pyglet import gl 44 45 46_fake_gl_attributes = { 47 'double_buffer': 0, 48 'stereo': 0, 49 'aux_buffers': 0, 50 'accum_red_size': 0, 51 'accum_green_size': 0, 52 'accum_blue_size': 0, 53 'accum_alpha_size': 0 54} 55 56class HeadlessConfig(Config): 57 def match(self, canvas): 58 if not isinstance(canvas, HeadlessCanvas): 59 raise RuntimeError('Canvas must be an instance of HeadlessCanvas') 60 61 display_connection = canvas.display._display_connection 62 63 # Construct array of attributes 64 attrs = [] 65 for name, value in self.get_gl_attributes(): 66 if name == 'double_buffer': 67 continue 68 attr = HeadlessCanvasConfig.attribute_ids.get(name, None) 69 if attr and value is not None: 70 attrs.extend([attr, int(value)]) 71 attrs.extend([EGL_SURFACE_TYPE, EGL_PBUFFER_BIT]) 72 attrs.extend([EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT]) 73 attrs.extend([EGL_NONE]) 74 attrs_list = (egl.EGLint * len(attrs))(*attrs) 75 76 num_config = egl.EGLint() 77 egl.eglChooseConfig(display_connection, attrs_list, None, 0, byref(num_config)) 78 configs = (egl.EGLConfig * num_config.value)() 79 egl.eglChooseConfig(display_connection, attrs_list, configs, 80 num_config.value, byref(num_config)) 81 82 result = [HeadlessCanvasConfig(canvas, c, self) for c in configs] 83 return result 84 85 86class HeadlessCanvasConfig(CanvasConfig): 87 attribute_ids = { 88 'buffer_size': egl.EGL_BUFFER_SIZE, 89 'level': egl.EGL_LEVEL, # Not supported 90 'red_size': egl.EGL_RED_SIZE, 91 'green_size': egl.EGL_GREEN_SIZE, 92 'blue_size': egl.EGL_BLUE_SIZE, 93 'alpha_size': egl.EGL_ALPHA_SIZE, 94 'depth_size': egl.EGL_DEPTH_SIZE, 95 'stencil_size': egl.EGL_STENCIL_SIZE, 96 'sample_buffers': egl.EGL_SAMPLE_BUFFERS, 97 'samples': egl.EGL_SAMPLES, 98 } 99 100 def __init__(self, canvas, egl_config, config): 101 super(HeadlessCanvasConfig, self).__init__(canvas, config) 102 self._egl_config = egl_config 103 context_attribs = (EGL_CONTEXT_MAJOR_VERSION, config.major_version or 2, 104 EGL_CONTEXT_MINOR_VERSION, config.minor_version or 0, 105 EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE, config.forward_compatible or 0, 106 EGL_CONTEXT_OPENGL_DEBUG, config.debug or 0, 107 EGL_NONE) 108 self._context_attrib_array = (egl.EGLint * len(context_attribs))(*context_attribs) 109 110 for name, attr in self.attribute_ids.items(): 111 value = egl.EGLint() 112 egl.eglGetConfigAttrib(canvas.display._display_connection, egl_config, attr, byref(value)) 113 setattr(self, name, value.value) 114 115 for name, value in _fake_gl_attributes.items(): 116 setattr(self, name, value) 117 118 def compatible(self, canvas): 119 # TODO check more 120 return isinstance(canvas, HeadlessCanvas) 121 122 def create_context(self, share): 123 return HeadlessContext(self, share) 124 125 126class HeadlessContext(Context): 127 def __init__(self, config, share): 128 super(HeadlessContext, self).__init__(config, share) 129 130 self.display_connection = config.canvas.display._display_connection 131 132 self.egl_context = self._create_egl_context(share) 133 if not self.egl_context: 134 raise gl.ContextException('Could not create GL context') 135 136 def _create_egl_context(self, share): 137 if share: 138 share_context = share.egl_context 139 else: 140 share_context = None 141 142 egl.eglBindAPI(egl.EGL_OPENGL_API) 143 return egl.eglCreateContext(self.config.canvas.display._display_connection, 144 self.config._egl_config, share_context, 145 self.config._context_attrib_array) 146 147 def attach(self, canvas): 148 if canvas is self.canvas: 149 return 150 151 super(HeadlessContext, self).attach(canvas) 152 153 self.egl_surface = canvas.egl_surface 154 self.set_current() 155 156 def set_current(self): 157 egl.eglMakeCurrent( 158 self.display_connection, self.egl_surface, self.egl_surface, self.egl_context) 159 super(HeadlessContext, self).set_current() 160 161 def detach(self): 162 if not self.canvas: 163 return 164 165 self.set_current() 166 gl.glFlush() # needs to be in try/except? 167 168 super(HeadlessContext, self).detach() 169 170 egl.eglMakeCurrent( 171 self.display_connection, 0, 0, None) 172 self.egl_surface = None 173 174 def destroy(self): 175 super(HeadlessContext, self).destroy() 176 if self.egl_context: 177 egl.eglDestroyContext(self.display_connection, self.egl_context) 178 self.egl_context = None 179 180 def flip(self): 181 if not self.egl_surface: 182 return 183 184 egl.eglSwapBuffers(self.display_connection, self.egl_surface) 185