【发布时间】:2012-02-27 20:12:16
【问题描述】:
我正在用 pyglet 开发一个小游戏。当然,其中一个核心是绘制彩色矩形。我最初是通过在内存中创建图像并blit()ing 来做到这一点的,效果很好。在注意到有多丑陋、迂回和低效(是的,我分析了 - ColorRect.draw() 花费了大量时间并通过此更改提高了 10 倍的效率)之后,我开始通过 pyglet.graphics.Batch 创建顶点列表(我复制了大部分示例之一的逐字代码)。从那时起,我在一些低级 OpenGL 代码中遇到了一个奇怪的异常,我无法找到原因或可靠地重现该异常。
与游戏事件没有明显的关系——例如,之前没有任何异常发生,或者我经常想念它。由于错误发生在事件循环的某个深处,我无法轻松追踪是哪个位置更新导致它。老实说,我很难过。因此,我将把我发现的东西都记下来,并希望有某种通灵者。
我已经在 Windows 7 32 位(我可能很快会在 Ubuntu 11.10 上尝试)使用 Python 3.2.2 进行了尝试,pyglet 版本为 043180b64260(从 Goggle Code 提取并从源代码构建,1.1 .4 版本更难安装,因为它不会自动运行 2to3,尽管它似乎同样支持 py3k)。接下来我可能会更新到最新的 mercurial 版本,但这只是几个提交,而且这些更改似乎完全无关。
完整的回溯(审查了一些不符合原则的路径,但请注意它在它自己的 virtualenv 中):
Traceback (most recent call last):
File "<my main file>", line 152, in <module>
main()
File "<my main file>", line 148, in main
run()
File "<my main file>", line 125, in run
pyglet.app.run()
File "<virtualenv>\Lib\site-packages\pyglet\app\__init__.py", line 123, in run
event_loop.run()
File "<virtualenv>\Lib\site-packages\pyglet\app\base.py", line 135, in run
self._run_estimated()
File "<virtualenv>\Lib\site-packages\pyglet\app\base.py", line 164, in _run_estimated
timeout = self.idle()
File "<virtualenv>\Lib\site-packages\pyglet\app\base.py", line 278, in idle
window.switch_to()
File "<virtualenv>\Lib\site-packages\pyglet\window\win32\__init__.py", line 305, in switch_to
self.context.set_current()
File "<virtualenv>\Lib\site-packages\pyglet\gl\win32.py", line 213, in set_current
super(Win32Context, self).set_current()
File "<virtualenv>\Lib\site-packages\pyglet\gl\base.py", line 320, in set_current
buffers = (gl.GLuint * len(buffers))(*buffers)
IndexError: invalid index
通过事后分析运行(积极地逐步执行代码,直到 FPS 从 60 降到 7 之前它碰巧是不可行的)pdb 显示:
-
buffers是一个整数列表;我不知道这些代表什么或它们来自哪里,但它们是从名为self.object_space._doomed_textures的列表中提取的(其中self是一个窗口对象)。相关的评论说这个代码块释放了计划删除的纹理。我认为我没有在任何地方明确使用纹理,但谁知道 pyglet 在引擎盖下做了什么。我假设这些整数是 ID 或要销毁的纹理。 -
gl.GLuint是ctypes.c_ulong的别名;因此(gl.GLuint * len(buffers))(*buffers)创建了一个长度和内容相同的ulong数组 - 我可以在
pdb提示符处评估完全相同的表达式,而不会出现错误或数据损坏。
使用 ctypes 的独立实验(在 virtualenv 之外并且没有导入 pyglet)表明,如果向数组构造函数提供了太多参数,则会引发 IndexError。这是没有意义的,实验和逻辑都表明长度和参数计数必须始终匹配。
- 还有其他可能发生此异常的情况吗?这可能是 pyglet 的错误,还是我滥用库并错过了相关的警告?
- 创建和维护顶点列表的代码在调试中有用吗?可能有东西有问题。我已经盯着它看了,但是由于我对
pyglet.graphics的经验很少,所以它的用处有限。如果您想查看ColorRect代码,请发表评论。 - 还有什么其他想法可能导致这种情况吗?
【问题讨论】:
-
你能让舒尔不使用多线程吗?如果多个线程处于活动状态,这可能会导致
base.py出现问题。buffers可能会被另一个线程更改,而在单独的 GLuint 构造函数运行以填充它之前分配数组。因此len(buffer)不会匹配(*buffers)长度。 -
@dronus 狡猾的主意!我没有在任何地方使用线程,我怀疑 pyglet 尝试并行运行。但我会检查
'_thread' in sys.modules。 -
@dronus 忘了
_thread,它显然总是被导入的(我检查了一个新的解释器会话)。另一方面,threading被导入到我的代码中,而不是在解释器会话中。threading.enumerate仅提供[<_MainThread(MainThread, started 4256)>]。我将添加一个断言始终正确,并调查谁导入了threading。 -
@dronus 好像是
logging导入threading。pyglet.app.base也是如此,但显然它只使用threading.Event和queue(依次导入dummy_threading导入threading)进行单线程管理。进程资源管理器还表明一直有一个python.exe线程。 -
抱歉,我认为这里不是解决方案,只是一些注释:1) 问题和 cmets 正在谈论
_doomed_textures但我认为一定是关于_doomed_buffers2)IndexError出现是因为代码试图分配一个元素少于原始元素的缓冲区,对吗? (问题是“...太多参数...”)3) 请注意pyglet.gl.base.py的第373 行以... and False:结尾,因此代码永远不会调用glDeleteBuffers。我想尝试删除最后一个条件并再次测试(解决方法?)。
标签: python debugging opengl ctypes pyglet