【问题标题】:Asynchronous image load in pygamepygame中的异步图像加载
【发布时间】: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)

【问题讨论】:

  • 您是否考虑过尝试将流图像存储为字符串中的像素数据,并将其作为Surfacepygame.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


【解决方案1】:

也许您可以尝试将图像从缓冲区对象转换为工作函数中更有效的对象(可能是表面),然后将该计算出的表面发送到输出队列? 这样,表面对象的生成不会阻塞主线程。

【讨论】:

  • 不幸的是,这不起作用,因为 pygame 只能在主进程中创建表面。我可以在一个单独的线程中制作表面,但由于 GIL,这仍然会在我的主线程中产生卡顿。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多