1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
# -*- coding: utf-8 -*-
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
"""This module provides a (functional) API to OpenGL ES 2.0.
There are multiple backend implementations of this API, available as
submodules of this module. One can use one of the backends directly,
or call `gl.use_gl()` to select one. The backend system allow running
visualizations using Angle, WebGL, or other forms of remote rendering.
This is in part possible by the widespread availability of OpenGL ES 2.0.
All functions that this API provides accept and return Python arguments
(no ctypes is required); strings are real strings and you can pass
data as numpy arrays. In general the input arguments are not checked
(for performance reasons). Each function results in exactly one OpenGL
API call, except when using the pyopengl backend.
The functions do not have docstrings, but most IDE's should provide you
with the function signature. For more documentation see
http://www.khronos.org/opengles/sdk/docs/man/
"""
# NOTE: modules in this package that start with one underscore are
# autogenerated, and should not be edited.
from __future__ import division
import os
from ...util import config, logger
from ._constants import * # noqa
from ._proxy import BaseGLProxy
# Variable that will hold the module corresponding to the current backend
# This variable is used in our proxy classes to call the right functions.
current_backend = None
class MainProxy(BaseGLProxy):
"""Main proxy for the GL ES 2.0 API.
The functions in this namespace always call into the correct GL
backend. Therefore these function objects can be safely stored for
reuse. However, for efficienty it would probably be better to store the
function name and then do ``func = getattr(gloo.gl, funcname)``.
"""
def __call__(self, funcname, returns, *args):
func = getattr(current_backend, funcname)
return func(*args)
# Instantiate proxy objects
proxy = MainProxy()
def use_gl(target=None):
"""Let Vispy use the target OpenGL ES 2.0 implementation
Also see ``vispy.use()``.
Parameters
----------
target : str
The target GL backend to use. Default gl2 or es2, depending on the platform.
Available backends:
* gl2 - Use ES 2.0 subset of desktop (i.e. normal) OpenGL
* gl+ - Use the desktop ES 2.0 subset plus all non-deprecated GL
functions on your system (requires PyOpenGL)
* es2 - Use the ES2 library (Angle/DirectX on Windows)
* pyopengl2 - Use ES 2.0 subset of pyopengl (for fallback and testing)
* dummy - Prevent usage of gloo.gl (for when rendering occurs elsewhere)
You can use vispy's config option "gl_debug" to check for errors
on each API call. Or, one can specify it as the target, e.g. "gl2
debug". (Debug does not apply to 'gl+', since PyOpenGL has its own
debug mechanism)
"""
target = target or default_backend.__name__.split(".")[-1]
target = target.replace('+', 'plus')
# Get options
target, _, options = target.partition(' ')
debug = config['gl_debug'] or 'debug' in options
# Select modules to import names from
try:
mod = __import__(target, globals(), level=1)
except ImportError as err:
msg = 'Could not import gl target "%s":\n%s' % (target, str(err))
raise RuntimeError(msg)
# Apply
global current_backend
current_backend = mod
_clear_namespace()
if 'plus' in target:
# Copy PyOpenGL funcs, extra funcs, constants, no debug
_copy_gl_functions(mod._pyopengl2, globals(), debug=debug)
_copy_gl_functions(mod, globals(), True, debug=debug)
else:
_copy_gl_functions(mod, globals(), debug=debug)
def _clear_namespace():
"""Clear names that are not part of the strict ES API"""
ok_names = set(default_backend.__dict__)
ok_names.update(['gl2', 'glplus']) # don't remove the module
NS = globals()
for name in list(NS.keys()):
if name.lower().startswith('gl'):
if name not in ok_names:
del NS[name]
def _copy_gl_functions(source, dest, constants=False, debug=False):
"""Inject all objects that start with 'gl' from the source
into the dest. source and dest can be dicts, modules or BaseGLProxy's.
"""
# Get dicts
if isinstance(source, BaseGLProxy):
s = {}
for key in dir(source):
s[key] = getattr(source, key)
source = s
elif not isinstance(source, dict):
source = source.__dict__
if not isinstance(dest, dict):
dest = dest.__dict__
# Copy names
funcnames = [name for name in source.keys() if name.startswith('gl')]
for name in funcnames:
if debug and name != 'glGetError':
dest[name] = make_debug_wrapper(source[name])
else:
dest[name] = source[name]
# Copy constants
if constants:
constnames = [name for name in source.keys() if name.startswith('GL_')]
for name in constnames:
dest[name] = source[name]
def _arg_repr(arg):
"""Get a useful (and not too large) represetation of an argument."""
r = repr(arg)
max = 40
if len(r) > max:
if hasattr(arg, 'shape'):
r = 'array:' + 'x'.join([repr(s) for s in arg.shape])
else:
r = r[:max-3] + '...'
return r
def make_debug_wrapper(fn):
def gl_debug_wrapper(*args):
# Log function call
argstr = ', '.join(map(_arg_repr, args))
logger.debug("%s(%s)" % (fn.__name__, argstr))
# Call function
ret = fn(*args)
# Log return value
if ret is not None:
if fn.__name__ == 'glReadPixels':
logger.debug(" <= %s[%s]" % (type(ret), len(ret)))
else:
logger.debug(" <= %s" % repr(ret))
# Check for errors (raises if an error occured)
check_error(fn.__name__)
# Return
return ret
gl_debug_wrapper.__name__ = fn.__name__ + '_debug_wrapper'
# Store reference to wrapped function just for introspection
gl_debug_wrapper._wrapped_function = fn
return gl_debug_wrapper
def check_error(when='periodic check'):
"""Check this from time to time to detect GL errors.
Parameters
----------
when : str
Shown in the exception to help the developer determine when
this check was done.
"""
errors = []
while True:
err = glGetError()
if err == GL_NO_ERROR or (errors and err == errors[-1]):
break
errors.append(err)
if errors:
msg = ', '.join([repr(ENUM_MAP.get(e, e)) for e in errors])
err = RuntimeError('OpenGL got errors (%s): %s' % (when, msg))
err.errors = errors
err.err = errors[-1] # pyopengl compat
raise err
def _fix_osmesa_gl_lib_if_testing():
"""
This functions checks if we a running test with the osmesa backends and
fix the GL library if needed.
Since we have to fix the VISPY_GL_LIB *before* any import from the gl
module, we have to run this here.
Test discovery utilities (like pytest) will try to import modules
before running tests, so we have to modify the GL lib really early.
The other solution would be to setup pre-run hooks for the test utility,
but there doesn't seem to be a standard way to do that (e.g. conftest.py
for py.test)
"""
test_name = os.getenv('_VISPY_TESTING_APP', None)
if test_name == 'osmesa':
from ...util.osmesa_gl import fix_osmesa_gl_lib
fix_osmesa_gl_lib()
_fix_osmesa_gl_lib_if_testing()
# Load default gl backend
from . import gl2 as default_backend # noqa
if default_backend._lib is None: # Probably Android or RPi
from . import es2 as default_backend # noqa
# Call use to start using our default backend
use_gl()
|