Dans le cadre de la transition du code de mon moteur de jeu vers Cython, je porte ma classe Vertex Buffer Object (Vbo). J'utilise cette classe Vbo pour envoyer des données de modèle 3D au GPU. Le code (vbo.pyx
) ressemble actuellement à ceci:Utilisation de contextlib avec cython
cimport gl
from enum import Enum
import contextlib
class VboTarget(Enum):
ARRAY = gl.GL_ARRAY_BUFFER
INDEX = gl.GL_ELEMENT_ARRAY_BUFFER
cdef class Vbo:
cdef readonly gl.GLuint id_
cdef readonly double[:] data
cdef readonly int target
def __init__(self, data=None, target=VboTarget.ARRAY):
gl.glewInit()
gl.glGenBuffers(1, &self.id_)
self.target = target.value
if data is not None:
self.data = data
@contextlib.contextmanager
def bind(self):
gl.glBindBuffer(self.target, self.id_)
try:
yield
finally:
gl.glBindBuffer(self.target, 0)
def set_data(self, new_data):
self.data = new_data
def update(self):#perform gpu update
with self.bind():
gl.glBufferData(self.target, self.data.nbytes, &self.data[0], gl.GL_DYNAMIC_DRAW)
Je voudrais utiliser contextlib
comme il ferait en sorte que le tampon de liaison et déliaison au GPU se produirait proprement et automatiquement. Le code de Cython compile sans erreur; Cependant, quand j'importer ce module cython dans mon code python, je reçois le message d'erreur suivant:
Traceback (most recent call last):
File "main.py", line 2, in <module>
import vbo
File "vbo.pyx", line 21, in init vbo (vbo.c:15766)
@contextlib.contextmanager
File "C:\Python27\lib\contextlib.py", line 82, in contextmanager
@wraps(func)
File "C:\Python27\lib\functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'method_descriptor' object has no attribute '__module__'
Je ne suis pas vraiment sûr de savoir comment interpréter ce message. Puis-je utiliser des décorateurs contextlib
avec un cdef class
et si oui, comment? Est-ce que contextlib
est même compatible avec Cython?
Mise à jour: Voici une version alternative en utilisant __enter__
et __exit__
à la place:
cimport gl
from enum import Enum
from cpython cimport array
import array
class VboTarget(Enum):
ARRAY = gl.GL_ARRAY_BUFFER
INDEX = gl.GL_ELEMENT_ARRAY_BUFFER
cdef class Vbo:
cdef readonly gl.GLuint id_
cdef readonly float[:] data
cdef readonly int target
def __init__(self, data=None, target=VboTarget.ARRAY):
gl.glewInit()
gl.glGenBuffers(1, &self.id_)
self.target = target.value
if data is not None:
self.data = data
def set_data(self, float[:] new_data):
self.data = new_data
def update(self):#perform gpu update
with self:
gl.glBufferData(self.target, self.data.nbytes, &self.data[0], gl.GL_STATIC_DRAW)
def __enter__(self):
gl.glBindBuffer(self.target, self.id_)
def __exit__(self, exc_type, exc_val, exc_tb):
gl.glBindBuffer(self.target, 0)
Si vous utilisez une classe normale en Cython au lieu d'une classe 'cdef', alors' contextlib' est presque certainement compatible. Je ne sais pas encore si ça peut fonctionner avec une classe 'cdef' - ça a l'air compliqué! – DavidW
@DavidW Si j'utilise une classe normale à la place, alors je ne serais pas capable d'utiliser n'importe quel type d'extension 'cdef' dans mon code, non? Les Vbos étaient l'un des goulets d'étranglement de performance que j'utilisais juste en python, donc je veux définitivement taper autant de variables que possible. Je peux travailler en utilisant 'contextlib' en utilisant' __enter__' et '__exit__' à la place avec ma classe' cdef', mais ensuite l'utilisateur appelle quelque chose comme 'avec my_vbo:' plutôt qu'avec 'my_vbo.bind()', qui n'est pas idéal. – CodeSurgeon
Oui - une classe normale n'a pas d'attributs typés. Je pense que vous pouvez renvoyer une classe normale avec les attributs '__enter__' et' __exit__' de 'bind()' à la place, ce qui conserverait l'interface que vous voulez et la classe 'cdef' (et nécessite simplement d'écrire un peu plus de code que d'utiliser contextlib – DavidW