无头Arcade#

对于某些应用程序,我们可能想要运行Arcade,而不是打开一个窗口。我们可能希望绘制到缓冲区并保存图像,以便在服务器或数据科学可视化中使用。在远程云操作中,我们甚至可能没有用于计算机的显示器。以这种方式运行Arcade被称为无头模式。

Arcade可以在 headless mode 在Linux服务器上使用 EGL 安装完毕。这应该可以在桌面环境和服务器上工作,甚至在虚拟机上工作。根据您的用例,软件和硬件渲染都应该是可接受的。

我们正在利用侏儒中的无头模式。如果你想了解无头机器人的内部工作原理,那是个不错的选择。

启用无头模式#

需要配置无头模式 在此之前 Arcade是导入的。这可以通过以下方式完成:

# Before arcade is imported
import os
os.environ["ARCADE_HEADLESS"] = "True"

# The above is a shortcut for
import pyglet
pyglet.options["headless"] = True

当然,这也意味着您可以在外部配置Headless。

$ export ARCADE_HEADLESS=True

要快速检查环境,如渲染器和版本::

$ python -m arcade

Arcade 2.6.12
-------------
vendor: AMD
renderer: AMD Radeon(TM) Vega 11 Graphics (RAVEN, DRM 3.41.0, 5.13.0-37-generic, LLVM 12.0.0)
version: (4, 6)
python: 3.9.9 (main, Dec 20 2021, 08:19:16)
[GCC 9.3.0]
platform: linux

这对我的代码有什么影响?#

在无头模式下,我们没有任何窗口事件或输入事件。这意味着像这样的事件 on_key_presson_mouse_motion 永远不会被召唤。不是为无头设置创建的项目需要进行一些调整。

在无头模式下,Arcade Window 将改为扩展侏儒的无头窗口。我们已经添加了一个属性 arcade.Window.headless (布尔)可以用来分离无头逻辑的。

请注意,窗口本身仍然有一个帧缓冲区,您可以对其进行渲染并从中读取像素。该帧缓冲区的大小是您在创建窗口时指定的大小。可以创建更多的帧缓冲区 ArcadeContext 如果需要的话。

警告

如果您要创建和销毁大量的街机对象,您可能需要查看 arcade.ArcadeContext.gc_mode 。在Arcade中,我们通常每帧对OpenGL对象进行一次垃圾回收,方法是调用 gc()

警告

如果要加载越来越多的纹理,可能需要清理纹理缓存。这仅用于缓存 arcade.Texture 物体。看见 cleanup_texture_cache() 。如果您在精灵上使用这些纹理,这也可能涉及将它们从全局纹理图集中移除。

示例#

有两种推荐的方法: 简单无头模式扩展Arcade窗口时的无头模式

简单无头模式#

对于更简单的应用程序,我们不需要对窗口进行子类化。

# Configure headless before importing arcade
import os
os.environ["ARCADE_HEADLESS"] = "true"
import arcade

# Create a 100 x 100 headless window
window = arcade.open_window(100, 100)

# Draw a quick rectangle
arcade.draw_rectangle_filled(50, 50, 50, 50, color=arcade.color.AMAZON)

# Dump the framebuffer to a png
image = arcade.get_image(0, 0, *window.get_size())
image.save(f"framebuffer.png")

你可以自由地 clear() 窗口并随时呈现新内容。

扩展Arcade窗口时的无头模式#

对于扩展窗口的Arcade用户来说,这种方法更有意义。这个 run() 方法支持无头模式,并将通过调用 on_updateon_drawflip() (交换缓冲区)循环,直到关闭窗口。

import os
os.environ["ARCADE_HEADLESS"] = "true"
import arcade

class App(arcade.Window):

    def __init__(self):
        super().__init__(200, 200)
        self.frame = 0
        self.sprite = arcade.Sprite(
            ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png",
            center_x=self.width / 2,
            center_y=self.height / 2,
        )

    def on_draw(self):
        self.clear()
        self.sprite.draw()

        # Dump the window framebuffer to disk
        image = arcade.get_image(0, 0, *self.get_size())
        image.save("framebuffer.png")

    def on_update(self, delta_time: float):
        # Close the window on the second frame
        if self.frame == 2:
            self.close()

        self.frame += 1

App().run()

您还可以将代码拆分为 arcade.View 类,如果需要的话。这样做可能会使在开发过程中使用无头和无头模式变得更简单。您只需以编程方式关闭窗口并切换视图。我们可以轻松地将逻辑与 arcade.Window.headless 旗帜。当呼叫时 run() 我们还对每一帧的OpenGL资源进行垃圾收集。

高级#

当然,更低级别的呈现API仍然可以通过 arcade.Window.ctx 。它公开了在OpenGL类型上创建帧缓冲区、纹理、着色器(包括计算着色器)和其他高级包装的方法。

在多GPU环境中工作时,您还可以选择特定的设备ID。默认情况下为0,必须在创建窗口之前进行设置。这些设备ID通常指的是物理设备(显卡)或虚拟卡/设备。

# Default setting
pyglet.options['headless_device'] = 0

# Use the second gpu/device
pyglet.options['headless_device'] = 1

有问题吗?#

如果您遇到问题或有问题,请在GitHub上创建问题或加入我们的不一致服务器。