Setup environment on MacOS

  • First, install the Vulkan SDK. I recommend downloading the latest version from the LunarG website. The default install path is usually /Users/$USER/VulkanSDK/version-number.
  • The SDK includes a script named setup-env.sh for setting environment variables, so you need to run it every time your shell starts.
  • Open your shell config file, such as /.zshrc for zsh or /.bash_profile for bash, and add the following line. This gives every new shell session the corresponding Vulkan environment variables, and CMake can also find Vulkan through find_package.
# Note: replace this path with your actual VulkanSDK installation path
source /Users/USER/VulkanSDK/1.3.xxx.x/setup-env.sh

MoltenVK

Apple’s native graphics API is Metal, and there is no native Vulkan implementation on macOS. So you need MoltenVK to translate Vulkan calls into Metal calls. If you installed the Vulkan SDK, MoltenVK is already included. One extra thing to watch out for: when creating Vulkan-related contexts, there are three places you need to configure.

  • When setting extensions in InstanceCreateInfo, also add VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME. This extension allows the Instance to enumerate devices that are not fully compliant with the Vulkan specification, such as devices implemented through MoltenVK.
  • When setting flags in InstanceCreateInfo, set VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR. Use it together with the extension above.
  • When setting EnabledExtension in DeviceCreateInfo, add "VK_KHR_portability_subset". Since MoltenVK does not implement every Vulkan feature, this device extension provides query interfaces for checking which features are limited.

ICD (Installable Client Driver)

The ICD file is a JSON file used by VulkanLoader to discover the actual location of MoltenVK. Its original location is under $ENV{VULKAN_SDK}/share/vulkan/icd.d. This JSON describes where MoltenVK is located.

{
    "file_format_version": "1.0.0",
    "ICD": {
        "library_path": "../../../lib/libMoltenVK.dylib",
        "api_version": "1.2.0",
        "is_portability_driver": true
    }
}

The way VulkanLoader loads MoltenVK is: it finds the ICD file, then uses the location pointed to by the ICD file to find MoltenVK, and from there finds the Vulkan implementation. VulkanLoader searches for ICD files in the following order (VulkanLoader):

<bundle>/Contents/Resources/vulkan/icd.d
~/.config/vulkan/icd.d
/etc/xdg/vulkan/icd.d
/usr/local/etc/vulkan/icd.d
/etc/vulkan/icd.d
~/.local/share/vulkan/icd.d
/usr/local/share/vulkan/icd.d
/usr/share/vulkan/icd.d

If the current program is an AppBundle and an ICD file is found under Resources, VulkanLoader will ignore values provided by environment variables, such as VK_ICD_FILENAMES. If it cannot find an ICD file in any of the paths above, it will then use the ICD file specified by the environment variable to discover MoltenVK. If the environment variable is not set either, MoltenVK cannot be found. The visible symptom is that enumerateInstanceLayerProperties returns very few extensions, only the VulkanLoader extensions, and may not even include Surface extensions. Creating an Instace directly will also fail and return VK_ERROR_INCOMPATIBLE_DRIVER.

If you use CMake locally to generate an XCode project, you will probably run into the issue above. VSCode debugging carries the environment variables with it, while XCode does not. So if you want to debug in XCode, you need to manually set the environment variables under Product > Scheme > Edit Scheme… > Run > Arguments.

If you are developing locally, running locally, and the local machine already has the Vulkan SDK installed with environment variables configured, then you do not really need to worry about this. But if you are building an AppBundle, you need to pay extra attention. The target machine most likely will not have the corresponding Vulkan SDK environment variables. Even if it does, the version may not match what you need. So you need to prepare the libraries yourself, and also create your own ICD file that points to those libraries. Specifically:

  1. Copy libvulkan.dylib, libvulkan.1.dylib, and libMoltenVK.dylib into Frameworks inside the AppBundle.
  2. Copy MoltenVK_icd.json from the Vulkan SDK into this path inside the AppBundle: \Resources\vulkan\icd.d. This lets VulkanLoader search that path and find the ICD file.
  3. Adjust the path value in the JSON so it points to libMoltenVK.dylib under Frameworks through a relative path. Then VulkanLoader can load MoltenVK correctly.

Of course, if you are not using an AppBundle, or if you want to customize the ICD file path, that is also fine. Just call setenv manually in code before any Vulkan initialization and set the VK_ICD_FILENAME environment variable to point to the corresponding ICD file.