【问题标题】:pyglet.graphics: IndexError on ctypes array creationpyglet.graphics:创建 ctypes 数组时出现 IndexError
【发布时间】: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.GLuintctypes.c_ulong 的别名;因此(gl.GLuint * len(buffers))(*buffers) 创建了一个长度和内容相同的ulong 数组
  • 我可以在pdb 提示符处评估完全相同的表达式,而不会出现错误或数据损坏。

使用 ctypes 的独立实验(在 virtualenv 之外并且没有导入 pyglet)表明,如果向数组构造函数提供了太多参数,则会引发 IndexError。这是没有意义的,实验和逻辑都表明长度和参数计数必须始终匹配。

  1. 还有其他可能发生此异常的情况吗?这可能是 pyglet 的错误,还是我滥用库并错过了相关的警告?
  2. 创建和维护顶点列表的代码在调试中有用吗?可能有东西有问题。我已经盯着它看了,但是由于我对pyglet.graphics 的经验很少,所以它的用处有限。如果您想查看ColorRect 代码,请发表评论。
  3. 还有什么其他想法可能导致这种情况吗?

【问题讨论】:

  • 你能让舒尔不使用多线程吗?如果多个线程处于活动状态,这可能会导致base.py 出现问题。 buffers 可能会被另一个线程更改,而在单独的 GLuint 构造函数运行以填充它之前分配数组。因此len(buffer) 不会匹配(*buffers) 长度。
  • @dronus 狡猾的主意!我没有在任何地方使用线程,我怀疑 pyglet 尝试并行运行。但我会检查'_thread' in sys.modules
  • @dronus 忘了_thread,它显然总是被导入的(我检查了一个新的解释器会话)。另一方面,threading 被导入到我的代码中,而不是在解释器会话中。 threading.enumerate 仅提供 [&lt;_MainThread(MainThread, started 4256)&gt;]。我将添加一个断言始终正确,并调查谁导入了threading
  • @dronus 好像是logging 导入threadingpyglet.app.base 也是如此,但显然它只使用threading.Eventqueue(依次导入dummy_threading 导入threading)进行单线程管理。进程资源管理器还表明一直有一个python.exe 线程。
  • 抱歉,我认为这里不是解决方案,只是一些注释:1) 问题和 cmets 正在谈论 _doomed_textures 但我认为一定是关于 _doomed_buffers 2) IndexError 出现是因为代码试图分配一个元素少于原始元素的缓冲区,对吗? (问题是“...太多参数...”)3) 请注意pyglet.gl.base.py 的第373 行以... and False: 结尾,因此代码永远不会调用glDeleteBuffers。我想尝试删除最后一个条件并再次测试(解决方法?)。

标签: python debugging opengl ctypes pyglet


【解决方案1】:

提供一个真正相关的答案有点困难,因为没有提供代码,但从错误输出中我可以看到。

buffers = (gl.GLuint * len(buffers))(*buffers)

因此,如果我理解正确,您将 GLuint 的大小(4 个字节)乘以实际缓冲区长度(如果已初始化)。也许这就是您的索引无效的原因,因为它太高了?

通常情况下没问题,因为缓冲区是以字节为单位的,但你说它是一个整数列表?

希望对你有帮助

【讨论】:

  • 我对ctypes的理解是some_c_type * some_int创建了some_int类型的some_c_type类型的项目(注意GLuint is c_ulong)。例如,c_long * 10 产生 &lt;class '__main__.c_long_Array_10'&gt;,并且可以使用 10 参数来实例化此类。尝试使用 11 个或更多参数来实例化它会导致 IndexError,传递超出范围的值只会静默截断(尝试 (c_byte * 10)(*range(4000, 4010)))。
  • 我对 ctypes 进行了更多研究,他们是这么说的。 Quote from "注意:一些代码示例引用了 ctypes c_int 类型。这种类型是 32 位系统上 c_long 类型的别名。因此,如果您期望 c_int 打印 c_long ,您不应该感到困惑 — 它们实际上是同一类型。”我不知道这个页面对你来说有多相关,但我敢打赌 uint 和 ulong 是一样的。
  • 好吧,我的意思是,那并不重要(GLuint 的类型),但我仍然不明白(如果缓冲区是相同类型的整数列表)为什么需要将此类型的大小与您的数组长度相乘。从我所看到的代码来看,我很难为您提供更多帮助。我只能建议对 pyglet 更加怀疑,并在独立项目中进行更多测试以对此进行调查(导入您的 virtualenv 和 pyglet)。
  • 将类型与缓冲区长度相乘会生成一个 ctypes 数组,这似乎是将整数传递给 OpenGL 所必需的。感谢您的帮助,但您似乎偏离了轨道。是的,我也担心 pyglet 可能有问题。但由于我已经设法通过其他方式实现了类似的改进,所以我并不热衷于在这个问题上花费更多时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-09
  • 2014-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多