Brief Overview of the Vulkan API

A quick outline of the technical details of Vulkan

In Comparing OpenGL ES and Vulkan, the general differences in design philosophy between OpenGL ES and Vulkan were compared. This section will go into a bit more detail about particular aspects of the Vulkan API which may be unfamiliar to an OpenGL ES developer. This includes discussion of object initialisation, managing framebuffers, the presentation engine, and the pipeline.

An important concept to understand before moving on to anything else is the Vulkan loader.

What is the Loader?

The Vulkan loader is used to connect an application with Vulkan-supporting hardware. This is usually one or more GPUs in the user's system. The loader sits between the application and one or more Installable Client Drivers (ICDs). An ICD is the software which interfaces with the hardware itself. The loader can also introduce special, optional functionality by using any number of Vulkan layers. Layers will be discussed in more detail in Layers and Extensions. When a Vulkan function is called in an application the loader determines how to dispatch it to the appropriate set of layers and ICDs.

Acquiring the Loader

There are two types of loader available: a desktop loader and a mobile loader. The desktop loader is currently targeted at Windows and Linux while the mobile loader is only available for Android and is also closed source. Either of these loaders can be acquired by installing a Vulkan application. This is can be through the OS, in the case of Android, but it can also be in a driver package or included in an SDK, such as LunarG Vulkan SDK.

Call Chains and Dispatch Tables

The Vulkan loader uses the concepts of call chains and dispatch tables to control the dispatching of function calls. The call chain is the sequence of function calls from the application to its final destination at the IDCs. Call-chains, in most cases, are started by a trampoline. A trampoline is an entry point for a command that triggers the proper call-chain start. As mentioned above, the loader also introduces a series of layers which can add functionality beyond the core Vulkan specification.

The application initially calls a Vulkan function in the trampoline. The trampoline sets up the chain and then calls the function in the first layer. This continues along the chain with each layer calling the function to the next one until it reaches the loader terminator. This code then distributes calls to multiple ICDs..

There are two types of call chains, an instance call chain and a device call chain. An instance call chain (illustrated above) has a loader terminator because it dispatches calls to multiple ICDs, whereas a device call chain (below) is for function which affect a specific device so there is no need for a terminator to distribute the calls.

A dispatch table is used at each point in the chain to track which function needs to be called in the next layer. Each layer has an individual dispatch table which is essentially just a collection of function pointers. Each layer creates a dispatch table by calling vkGetInstanceProcAddr or vkGetDeviceProcAddr in the next layer to retrieve a list of function pointers. If a layer does not implement any functionality for a specific function then it can simply call vkGetInstanceProcAddr/vkGetDeviceProcAddr in the next layer and then pass that back. This means certain layers can be avoided in the chain.

To get the best performance, the trampoline can be scrapped altogether by using vkGetDeviceProcAddr in the application. This means the developer must create their own dispatch table, making the application run faster.