Difficult-Rocket/libs/pyglet/gl/xlib.py

247 lines
8.7 KiB
Python
Raw Permalink Normal View History

2021-04-16 23:21:06 +08:00
import warnings
from ctypes import *
2022-12-09 21:05:32 +08:00
from .base import Config, CanvasConfig, Context
2021-04-16 23:21:06 +08:00
from pyglet.canvas.xlib import XlibCanvas
from pyglet.gl import glx
2021-09-05 00:50:05 +08:00
from pyglet.gl import glxext_arb
from pyglet.gl import glx_info
2021-04-16 23:21:06 +08:00
from pyglet.gl import glxext_mesa
from pyglet.gl import lib
from pyglet import gl
2021-04-16 23:21:06 +08:00
class XlibConfig(Config):
2022-12-09 21:05:32 +08:00
2021-04-16 23:21:06 +08:00
def match(self, canvas):
if not isinstance(canvas, XlibCanvas):
raise RuntimeError('Canvas must be an instance of XlibCanvas')
x_display = canvas.display._display
x_screen = canvas.display.x_screen
info = glx_info.GLXInfo(x_display)
# Construct array of attributes
attrs = []
for name, value in self.get_gl_attributes():
2022-12-09 21:05:32 +08:00
attr = XlibCanvasConfig.attribute_ids.get(name, None)
2021-04-16 23:21:06 +08:00
if attr and value is not None:
attrs.extend([attr, int(value)])
2022-12-09 21:05:32 +08:00
attrs.extend([glx.GLX_X_RENDERABLE, True])
2021-04-16 23:21:06 +08:00
attrs.extend([0, 0]) # attrib_list must be null terminated
2022-12-09 21:05:32 +08:00
2021-04-16 23:21:06 +08:00
attrib_list = (c_int * len(attrs))(*attrs)
2022-12-09 21:05:32 +08:00
elements = c_int()
configs = glx.glXChooseFBConfig(x_display, x_screen, attrib_list, byref(elements))
if not configs:
return []
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
configs = cast(configs, POINTER(glx.GLXFBConfig * elements.value)).contents
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
result = [XlibCanvasConfig(canvas, info, c, self) for c in configs]
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
# Can't free array until all XlibGLConfig's are GC'd. Too much
# hassle, live with leak. XXX
# xlib.XFree(configs)
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
return result
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
class XlibCanvasConfig(CanvasConfig):
2021-04-16 23:21:06 +08:00
attribute_ids = {
'buffer_size': glx.GLX_BUFFER_SIZE,
'level': glx.GLX_LEVEL, # Not supported
'double_buffer': glx.GLX_DOUBLEBUFFER,
'stereo': glx.GLX_STEREO,
'aux_buffers': glx.GLX_AUX_BUFFERS,
'red_size': glx.GLX_RED_SIZE,
'green_size': glx.GLX_GREEN_SIZE,
'blue_size': glx.GLX_BLUE_SIZE,
'alpha_size': glx.GLX_ALPHA_SIZE,
'depth_size': glx.GLX_DEPTH_SIZE,
'stencil_size': glx.GLX_STENCIL_SIZE,
'accum_red_size': glx.GLX_ACCUM_RED_SIZE,
'accum_green_size': glx.GLX_ACCUM_GREEN_SIZE,
'accum_blue_size': glx.GLX_ACCUM_BLUE_SIZE,
'accum_alpha_size': glx.GLX_ACCUM_ALPHA_SIZE,
'sample_buffers': glx.GLX_SAMPLE_BUFFERS,
'samples': glx.GLX_SAMPLES,
# Not supported in current pyglet API:
'render_type': glx.GLX_RENDER_TYPE,
'config_caveat': glx.GLX_CONFIG_CAVEAT,
'transparent_type': glx.GLX_TRANSPARENT_TYPE,
'transparent_index_value': glx.GLX_TRANSPARENT_INDEX_VALUE,
'transparent_red_value': glx.GLX_TRANSPARENT_RED_VALUE,
'transparent_green_value': glx.GLX_TRANSPARENT_GREEN_VALUE,
'transparent_blue_value': glx.GLX_TRANSPARENT_BLUE_VALUE,
'transparent_alpha_value': glx.GLX_TRANSPARENT_ALPHA_VALUE,
# Used internally
'x_renderable': glx.GLX_X_RENDERABLE,
2022-12-09 21:05:32 +08:00
}
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
def __init__(self, canvas, info, fbconfig, config):
super().__init__(canvas, config)
self.glx_info = info
self.fbconfig = fbconfig
2021-04-16 23:21:06 +08:00
for name, attr in self.attribute_ids.items():
value = c_int()
2022-12-09 21:05:32 +08:00
result = glx.glXGetFBConfigAttrib(canvas.display._display, self.fbconfig, attr, byref(value))
2021-04-16 23:21:06 +08:00
if result >= 0:
setattr(self, name, value.value)
def get_visual_info(self):
2022-12-09 21:05:32 +08:00
return glx.glXGetVisualFromFBConfig(self.canvas.display._display, self.fbconfig).contents
2021-04-16 23:21:06 +08:00
def create_context(self, share):
2022-12-09 21:05:32 +08:00
return XlibContext(self, share)
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
def compatible(self, canvas):
# TODO check more
return isinstance(canvas, XlibCanvas)
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
def _create_glx_context(self, share):
raise NotImplementedError('abstract')
def is_complete(self):
return True
class XlibContext(Context):
2021-04-16 23:21:06 +08:00
def __init__(self, config, share):
2022-12-09 21:05:32 +08:00
super().__init__(config, share)
2021-04-16 23:21:06 +08:00
self.x_display = config.canvas.display._display
self.glx_context = self._create_glx_context(share)
if not self.glx_context:
# TODO: Check Xlib error generated
raise gl.ContextException('Could not create GL context')
self._have_SGI_video_sync = config.glx_info.have_extension('GLX_SGI_video_sync')
self._have_SGI_swap_control = config.glx_info.have_extension('GLX_SGI_swap_control')
self._have_EXT_swap_control = config.glx_info.have_extension('GLX_EXT_swap_control')
self._have_MESA_swap_control = config.glx_info.have_extension('GLX_MESA_swap_control')
# In order of preference:
# 1. GLX_EXT_swap_control (more likely to work where video_sync will not)
# 2. GLX_MESA_swap_control (same as above, but supported by MESA drivers)
# 3. GLX_SGI_video_sync (does not work on Intel 945GM, but that has EXT)
# 4. GLX_SGI_swap_control (cannot be disabled once enabled)
self._use_video_sync = (self._have_SGI_video_sync and
not (self._have_EXT_swap_control or self._have_MESA_swap_control))
2022-12-09 21:05:32 +08:00
# XXX Mandate that vsync defaults on across all platforms.
2021-04-16 23:21:06 +08:00
self._vsync = True
2022-12-09 21:05:32 +08:00
self.glx_window = None
2021-04-16 23:21:06 +08:00
def is_direct(self):
return glx.glXIsDirect(self.x_display, self.glx_context)
def _create_glx_context(self, share):
if share:
share_context = share.glx_context
else:
share_context = None
2022-12-09 21:05:32 +08:00
attribs = []
if self.config.major_version is not None:
attribs.extend([glxext_arb.GLX_CONTEXT_MAJOR_VERSION_ARB, self.config.major_version])
if self.config.minor_version is not None:
attribs.extend([glxext_arb.GLX_CONTEXT_MINOR_VERSION_ARB, self.config.minor_version])
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
if self.config.opengl_api == "gl":
attribs.extend([glxext_arb.GLX_CONTEXT_PROFILE_MASK_ARB, glxext_arb.GLX_CONTEXT_CORE_PROFILE_BIT_ARB])
elif self.config.opengl_api == "gles":
attribs.extend([glxext_arb.GLX_CONTEXT_PROFILE_MASK_ARB, glxext_arb.GLX_CONTEXT_ES2_PROFILE_BIT_EXT])
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
flags = 0
if self.config.forward_compatible:
flags |= glxext_arb.GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
if self.config.debug:
flags |= glxext_arb.GLX_CONTEXT_DEBUG_BIT_ARB
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
if flags:
attribs.extend([glxext_arb.GLX_CONTEXT_FLAGS_ARB, flags])
attribs.append(0)
attribs = (c_int * len(attribs))(*attribs)
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
return glxext_arb.glXCreateContextAttribsARB(self.config.canvas.display._display,
self.config.fbconfig, share_context, True, attribs)
2021-04-16 23:21:06 +08:00
def attach(self, canvas):
if canvas is self.canvas:
return
2022-12-09 21:05:32 +08:00
super().attach(canvas)
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
self.glx_window = glx.glXCreateWindow(self.x_display, self.config.fbconfig, canvas.x_window, None)
2021-04-16 23:21:06 +08:00
self.set_current()
def set_current(self):
2022-12-09 21:05:32 +08:00
glx.glXMakeContextCurrent(self.x_display, self.glx_window, self.glx_window, self.glx_context)
super().set_current()
2021-04-16 23:21:06 +08:00
def detach(self):
if not self.canvas:
return
self.set_current()
2022-12-09 21:05:32 +08:00
gl.glFlush()
2021-04-16 23:21:06 +08:00
2022-12-09 21:05:32 +08:00
super().detach()
2021-04-16 23:21:06 +08:00
glx.glXMakeContextCurrent(self.x_display, 0, 0, None)
if self.glx_window:
glx.glXDestroyWindow(self.x_display, self.glx_window)
self.glx_window = None
def destroy(self):
2022-12-09 21:05:32 +08:00
super().destroy()
2021-04-16 23:21:06 +08:00
if self.glx_window:
glx.glXDestroyWindow(self.config.display._display, self.glx_window)
self.glx_window = None
if self.glx_context:
glx.glXDestroyContext(self.x_display, self.glx_context)
self.glx_context = None
2022-12-09 21:05:32 +08:00
def set_vsync(self, vsync=True):
self._vsync = vsync
interval = vsync and 1 or 0
try:
if not self._use_video_sync and self._have_EXT_swap_control:
glxext_arb.glXSwapIntervalEXT(self.x_display, glx.glXGetCurrentDrawable(), interval)
elif not self._use_video_sync and self._have_MESA_swap_control:
glxext_mesa.glXSwapIntervalMESA(interval)
elif self._have_SGI_swap_control:
glxext_arb.glXSwapIntervalSGI(interval)
except lib.MissingFunctionException as e:
warnings.warn(str(e))
def get_vsync(self):
return self._vsync
def _wait_vsync(self):
if self._vsync and self._have_SGI_video_sync and self._use_video_sync:
count = c_uint()
glxext_arb.glXGetVideoSyncSGI(byref(count))
glxext_arb.glXWaitVideoSyncSGI(2, (count.value + 1) % 2, byref(count))
2021-04-16 23:21:06 +08:00
def flip(self):
if not self.glx_window:
return
if self._vsync:
self._wait_vsync()
2022-12-09 21:05:32 +08:00
glx.glXSwapBuffers(self.x_display, self.glx_window)