I have just added two minimal, mostly self-contained cross-platform headless Vulkan examples to my open source C++ Vulkan repository. Unlike the other examples in my repository these two don’t require a surface (created from a window) and as such can be run on systems with no window compositor.
The intention behind the two examples is to show how Vulkan can be used for running graphics and compute tasks without the need for an actual user interface to be present i.e. in the form of a window, often called “headless mode”. Those could e.g. be run on headless cloud-hosted server instances (like AWS once they add support) with Vulkan support.
As these examples won’t display anything a lot of the things required by the other examples is not required, making the code required to get these running much smaller, and as such I did not went with the example base class but tried to make the examples as self-contained as possible with only minimal dependencies and a linear code structure in the main constructor.
The headless examples don’t require platform specific surfaces, which results in far less platform specific code paths. Only a few #ifdefs are required for Android platform handling, but that’s about it. Running without a display also means there’s no need for a swap chain.
The other big change is application life-cycle. These examples won’t run continuously. They’ll do the required setup, submit work to their compute or graphics queue once, read back the results and quit.
Note that on Android there’s no easy way to display a message box from NDK applications, so all output goes to logcat.
The source can be found here.
Compute shaders in Vulkan are mandatory and therefore are available on every Vulkan implementation. In a headless scenario compute shaders will often be used to do run calculations on a given set of data using the power of a dedicated GPU and this example implements such a scenario for a simple calculation of the fibonacci row.
The example fills an input buffer and runs a compute shader that calculates the fibonacci number for the current value and writes it back to the buffer:
This example could easily be expanded to do run more complex calculations with a performance boost from using shared compute shader memory along with a separate output buffer.
The source can be found here.
This example renders a scene to a (non-visible) frame buffer attachment, copies the results from VRAM to host (using a host visible linear tiled image) and stores the result to disk.
Due to the explicit nature of Vulkan, esp. in terms of synchronization a big part of this example are the multiple image barriers required to actually get the rendered image into the host buffer. The example contains all the synchronization required for this process and has been tested on multiple platforms and devices (including Android mobiles).
If run, the example sets up a frame buffer with a color and depth attachment, creates a render pass for this, renders a simple scene, does all required image transitions, copies the frame buffer color attachment to a host visible buffer and stores the result:
Will output the frame buffer color attachment to a file named “headless.ppm”. On desktop platforms the file is put into the working directory of the binary, on Android the file is stored to the sd card mount (which points to internal storage if no sd card is present).
The result will look like this: