【发布时间】:2014-03-07 09:53:03
【问题描述】:
我正在使用 pygame 在 python 2.7 中编写一个小应用程序,我想在其中平滑地在屏幕上显示动画图块。我在 Ubuntu 上写作,但如果相关的话,目标平台是 Raspberry Pi。挑战在于这些动画贴图的纹理存储在 Web 服务器上,并且随着时间的推移动态加载,而不是一次全部加载。我希望能够将这些图像加载到 pygame 中,而我的动画或输入响应中没有明显的障碍。加载频率非常低,就像每 30 秒抓取几张 jpg 文件一样。如果这意味着主输入/动画线程保持畅通,我愿意等待很长时间才能在后台加载图像。
因此,使用多处理模块,我能够将图像从服务器异步下载到缓冲区中,然后通过 multiprocessing.queues.SimpleQueue 对象将此缓冲区传递给我的主 pygame 进程。但是,一旦在 pygame 进程中可以访问缓冲区,当缓冲区加载到 Surface 以通过 pygame.image.frombuffer() 进行 blitting 时,我的应用程序中仍然存在故障。
有没有办法让这个 pygame.image.load() 调用异步,这样我在游戏中的动画等就不会被阻塞?由于 GIL,我想不出一个明显的解决方案。
如果我用 C 语言编写常规的 OpenGL 程序,我将能够使用像素缓冲区对象将数据异步写入 GPU,对吗? pygame 是否有机会公开此 API 的任何部分?我似乎在 pygame 文档中找不到它,我很陌生,所以如果答案很明显,请原谅我。任何指出 pygame 的术语如何转换为 OpenGL API 的帮助都会有很大的帮助,任何 pygame 可以异步初始化纹理的相关示例都会非常棒!
如果 pygame 不提供此功能,我有什么选择?有没有办法用 PySDL2 做到这一点?
编辑:好的,所以我尝试使用 pygame.image.frombuffer,它并没有真正减少我看到的搭便车。关于如何使这个图像加载真正异步的任何想法?这是一些代码 sn-ps 说明我目前正在做什么。
这是我拥有的异步代码,它位于与 pygame 不同的进程中
def _worker(in_queue, out_queue):
done = False
while not done:
if not in_queue.empty():
obj = in_queue.get()
# if a bool is passed down the queue, set the done flag
if isinstance(obj, bool):
done = obj
else:
url, w, h = obj
# grab jpg at given url. It is compressed so we use PIL to parse it
jpg_encoded_str = urllib2.urlopen(url).read()
# PIL.ImageFile
parser = ImageFile.Parser()
parser.feed(jpg_encoded_str)
pil_image = parser.close()
buff = pil_image.tostring()
# place decompressed buffer into queue for consumption by main thread
out_queue.put((url, w, h, buff))
# and so I create a subprocess that runs _worker function
这是我在主线程中运行的更新循环。它会查看 _Worker 进程是否已将任何内容放入 out_queue,如果是,则将其加载到 pygame 中:
def update():
if not out_queue.empty():
url, w, h, buff = img_buffer_queue.get()
# This call is where I get a hitch in my application
image = pygame.image.frombuffer(buff, (w, h), "RGB")
# Place loaded image on the pygame.Sprite, etc in the callback
callback = on_load_callbacks.pop(url, None)
if callback:
callback(image, w, h)
【问题讨论】:
-
您是否考虑过尝试将流图像存储为字符串中的像素数据,并将其作为
Surface到pygame.image.frombuffer加载? pygame.org/docs/ref/image.html#pygame.image.frombuffer -
我会试试看的。它可能会更快,但不是真正的异步。每次我预加载一个批次时,我可能只需要做更大的批次并且有一个小的“加载......”暂停。但这很蹩脚! :D
-
@Haz 好的,所以我尝试了 frombuffer() 无济于事,在图像加载过程中仍然遇到很大问题。请参阅对主帖的编辑。
-
你可以使用 PyOpenGL 之类的东西来获取 OpenGL 上下文,但我怀疑你必须在所有绘图中使用它。另一种可能性是创建一个与流图像大小匹配的空表面,然后在每个更新周期将几行像素复制到新表面,直到完成。流式传输单个图像可能需要更长的时间,但每帧的影响也可能较小。
-
一些问题:你为什么不使用线程而不是多处理?如果您使用线程,您将拥有共享内存,并且您可以从工作线程中的缓冲区加载图像? (这是 Raspberry Pi 的事情) from_buffer 调用线程也是安全的吗?您可以将图像的加载移动到 on_idle 回调吗?您是否可以控制 pygame 中的主循环,以便您可以控制何时从缓冲区加载图像。
标签: python multithreading opengl pygame game-engine