作为将我的游戏引擎代码转换为cython的一部分,我正在移植我的顶点缓冲对象(Vbo)类。我使用这个Vbo类将3D模型数据发送到GPU。代码(vbo.pyx
)目前看起来像这样:
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)
我想使用contextlib
,因为它可以确保缓冲区绑定和解除绑定到GPU会干净自动地发生。 cython代码编译没有错误;但是,当我将这个cython模块导入我的python代码时,我收到以下错误消息:
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__'
我不确定如何解释此消息。我可以将contextlib
装饰器与cdef class
一起使用,如果是,如何使用? contextlib
是否与cython兼容?
更新:
以下是使用__enter__
和__exit__
代替的替代版本:
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)
答案 0 :(得分:2)
尝试过简化版本的代码后,它似乎对我有用(Cython 0.25.1,Python 3.6.1):
import contextlib
cdef class Vbo:
def __init__(self):
pass
@contextlib.contextmanager
def bind(self):
self.do_something()
try:
yield
finally:
print("Finally")
def do_something(self):
print("something")
我认为对于更复杂的示例的任何更改都不会对此产生影响,但我没有gl.pxd
因此很难进行测试。可能值得确保您的Cython版本是最新的(如果您还没有)......
编辑:我认为重要的区别可能是Python 2.7与Python 3.6。 Python 3.6 has an AttributeError
catch block而Python 2.7 doesn't catch the error。因此,我不认为这是Cython行为的变化,因此可能不是真正的错误。
正如评论中所述,您可以使用非cdef
class
与__enter__
和__exit__
来获得相同的行为:
cdef class Vbo:
def __init__(self):
pass
def bind(self):
class C:
def __enter__(self2):
# note that I can access "self" from the enclosing function
# provided I rename the parameter passed to __enter__
self.do_something() # gl.BindBuffer(self.target, self.id_) for you
def __exit__(self2, exc_type, exc_val, exc_tb):
print("Done") # gl.glBindBuffer(self.target, 0)
return C()
def do_something(self):
print("something")
总而言之 - 我无法重现你的问题,但这里有另一种选择......
答案 1 :(得分:1)
现在看起来这对于最新的Cython master(Cython版本0.26b0)来说实际上并不是问题。只要binding=True
提示应用于源文件的顶部,@ DavidW的答案中描述的contextlib代码的简化版本就可以完美运行。可以找到关于此Cython问题的讨论here。