Asynchronous screenshot grabbing

At the moment, there are a number of ways to get a snapshot out of the framebuffer back in CPU memory, but all of them suffer from the problem that they require the GPU to finish rendering all the work up to the current moment so that the image can be downloaded from GPU to CPU memory.

It gets even worse when you want to save the image to disk. This can be quite expensive, even take over 100 ms, so you really want to do this in a thread if you don’t want to cause a quite visible chug in the framerate.

I’ve just made a change to Panda that makes it possible to grab a screenshot without blocking the application. It asks the driver to copy the current framebuffer contents to a driver-managed buffer in CPU memory, which can happen in the background using DMA. A couple of frames later, when this is done, Panda will spawn a task in a threaded task chain to copy the data out of this buffer and into a Texture object. Panda can also use this opportunity to write the file to disk for you if you want, so that this happens in a C++ thread as well.

The main interface is on GraphicsOutput, you can now do:

def callback(request):
    tex = request.result()
    # do something with tex

fut = base.win.get_async_screenshot()
fut.add_done_callback(callback)

Of course, you can also just await the return value in a coroutine and get the texture directly.

To write something to disk:

base.win.save_async_screenshot("screenshot.jpg")

This method also returns a future you can await or register events/callbacks with.

The high level base.screenshot() method is also extended with an extra argument to make it use the async version. Just pass in blocking=False. A “screenshot” event is sent with the filename as argument when it’s done, or you can use the returned future object.

This is available in the latest development builds.

7 Likes