Vulkan guide

This guide is intended to fill the gaps between the official Vulkan resources and the rest of the GLFW documentation and is not a replacement for either. It assumes some familiarity with Vulkan concepts like loaders, devices, queues and surfaces and leaves it to the Vulkan documentation to explain the details of Vulkan functions.

To develop for Vulkan you should download the LunarG Vulkan SDK for your platform. Apart from headers and link libraries, they also provide the validation layers necessary for development.

The Vulkan Tutorial has more information on how to use GLFW and Vulkan. The Khronos Vulkan Samples also use GLFW, although with a small framework in between.

For details on a specific Vulkan support function, see the Vulkan support reference. There are also guides for the other areas of the GLFW API.

Finding the Vulkan loader

GLFW itself does not ever need to be linked against the Vulkan loader.

By default, GLFW will load the Vulkan loader dynamically at runtime via its standard name: vulkan-1.dll on Windows, libvulkan.so.1 on Linux and other Unix-like systems and libvulkan.1.dylib on macOS.

macOS: GLFW will also look up and search the executable subdirectory of your application bundle.

If your code is using a Vulkan loader with a different name or in a non-standard location you will need to direct GLFW to it. Pass your version of vkGetInstanceProcAddr to glfwInitVulkanLoader before initializing GLFW and it will use that function for all Vulkan entry point retrieval. This prevents GLFW from dynamically loading the Vulkan loader.

glfwInitVulkanLoader(vkGetInstanceProcAddr);
void glfwInitVulkanLoader(PFN_vkGetInstanceProcAddr loader)
Sets the desired Vulkan vkGetInstanceProcAddr function.

macOS: To make your application be redistributable you will need to set up the application bundle according to the LunarG SDK documentation. This is explained in more detail in the SDK documentation for macOS.

Including the Vulkan header file

To have GLFW include the Vulkan header, define GLFW_INCLUDE_VULKAN before including the GLFW header.

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

If you instead want to include the Vulkan header from a custom location or use your own custom Vulkan header then do this before the GLFW header.

#include <path/to/vulkan.h>
#include <GLFW/glfw3.h>
The header of the GLFW 3 API.

Unless a Vulkan header is included, either by the GLFW header or above it, the following GLFW functions will not be declared, as depend on Vulkan types.

The VK_USE_PLATFORM_*_KHR macros do not need to be defined for the Vulkan part of GLFW to work. Define them only if you are using these extensions directly.

Querying for Vulkan support

If you are linking directly against the Vulkan loader then you can skip this section. The canonical desktop loader library exports all Vulkan core and Khronos extension functions, allowing them to be called directly.

If you are loading the Vulkan loader dynamically instead of linking directly against it, you can check for the availability of a loader and ICD with glfwVulkanSupported.

{
// Vulkan is available, at least for compute
}
int glfwVulkanSupported(void)
Returns whether the Vulkan loader and an ICD have been found.

This function returns GLFW_TRUE if the Vulkan loader and any minimally functional ICD was found.

If one or both were not found, calling any other Vulkan related GLFW function will generate a GLFW_API_UNAVAILABLE error.

Querying Vulkan function pointers

To load any Vulkan core or extension function from the found loader, call glfwGetInstanceProcAddress. To load functions needed for instance creation, pass NULL as the instance.

PFN_vkCreateInstance pfnCreateInstance = (PFN_vkCreateInstance)
glfwGetInstanceProcAddress(NULL, "vkCreateInstance");
GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char *procname)
Returns the address of the specified Vulkan instance function.

Once you have created an instance, you can load from it all other Vulkan core functions and functions from any instance extensions you enabled.

PFN_vkCreateDevice pfnCreateDevice = (PFN_vkCreateDevice)
glfwGetInstanceProcAddress(instance, "vkCreateDevice");

This function in turn calls vkGetInstanceProcAddr. If that fails, the function falls back to a platform-specific query of the Vulkan loader (i.e. dlsym or GetProcAddress). If that also fails, the function returns NULL. For more information about vkGetInstanceProcAddr, see the Vulkan documentation.

Vulkan also provides vkGetDeviceProcAddr for loading device-specific versions of Vulkan function. This function can be retrieved from an instance with glfwGetInstanceProcAddress.

PFN_vkGetDeviceProcAddr pfnGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)
glfwGetInstanceProcAddress(instance, "vkGetDeviceProcAddr");

Device-specific functions may execute a little bit faster, due to not having to dispatch internally based on the device passed to them. For more information about vkGetDeviceProcAddr, see the Vulkan documentation.

Querying required Vulkan extensions

To do anything useful with Vulkan you need to create an instance. If you want to use Vulkan to render to a window, you must enable the instance extensions GLFW requires to create Vulkan surfaces.

To query the instance extensions required, call glfwGetRequiredInstanceExtensions.

uint32_t count;
const char** extensions = glfwGetRequiredInstanceExtensions(&count);
const char ** glfwGetRequiredInstanceExtensions(uint32_t *count)
Returns the Vulkan instance extensions required by GLFW.

These extensions must all be enabled when creating instances that are going to be passed to glfwGetPhysicalDevicePresentationSupport and glfwCreateWindowSurface. The set of extensions will vary depending on platform and may also vary depending on graphics drivers and other factors.

If it fails it will return NULL and GLFW will not be able to create Vulkan window surfaces. You can still use Vulkan for off-screen rendering and compute work.

If successful the returned array will always include VK_KHR_surface, so if you don't require any additional extensions you can pass this list directly to the VkInstanceCreateInfo struct.

VkInstanceCreateInfo ici;
memset(&ici, 0, sizeof(ici));
ici.enabledExtensionCount = count;
ici.ppEnabledExtensionNames = extensions;
...

Additional extensions may be required by future versions of GLFW. You should check whether any extensions you wish to enable are already in the returned array, as it is an error to specify an extension more than once in the VkInstanceCreateInfo struct.

Querying for Vulkan presentation support

Not every queue family of every Vulkan device can present images to surfaces. To check whether a specific queue family of a physical device supports image presentation without first having to create a window and surface, call glfwGetPhysicalDevicePresentationSupport.

if (glfwGetPhysicalDevicePresentationSupport(instance, physical_device, queue_family_index))
{
// Queue family supports image presentation
}
int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily)
Returns whether the specified queue family can present images.

The VK_KHR_surface extension additionally provides the vkGetPhysicalDeviceSurfaceSupportKHR function, which performs the same test on an existing Vulkan surface.

Creating the window

Unless you will be using OpenGL or OpenGL ES with the same window as Vulkan, there is no need to create a context. You can disable context creation with the GLFW_CLIENT_API hint.

GLFWwindow* window = glfwCreateWindow(640, 480, "Window Title", NULL, NULL);
#define GLFW_NO_API
Definition: glfw3.h:1111
GLFWwindow * glfwCreateWindow(int width, int height, const char *title, GLFWmonitor *monitor, GLFWwindow *share)
Creates a window and its associated context.
struct GLFWwindow GLFWwindow
Opaque window object.
Definition: glfw3.h:1363
#define GLFW_CLIENT_API
Context client API hint and attribute.
Definition: glfw3.h:1018
void glfwWindowHint(int hint, int value)
Sets the specified window hint to the desired value.

See Windows without contexts for more information.

Creating a Vulkan window surface

You can create a Vulkan surface (as defined by the VK_KHR_surface extension) for a GLFW window with glfwCreateWindowSurface.

VkSurfaceKHR surface;
VkResult err = glfwCreateWindowSurface(instance, window, NULL, &surface);
if (err)
{
// Window surface creation failed
}
VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow *window, const VkAllocationCallbacks *allocator, VkSurfaceKHR *surface)
Creates a Vulkan surface for the specified window.

If an OpenGL or OpenGL ES context was created on the window, the context has ownership of the presentation on the window and a Vulkan surface cannot be created.

It is your responsibility to destroy the surface. GLFW does not destroy it for you. Call vkDestroySurfaceKHR function from the same extension to destroy it.