How to call vkCreateMacOSSurfaceMVK from Vulkan library?

65 Views Asked by At

I try to create a small app based on Vulkan v1.3.275.0 (and MoltenVK driver for macOS).

I successfully bundle my app into *.app with recommended Vulkan structure for macOS. My app opens a window using metal-cpp library and creates a Vulkan instance using MoltenVK driver.

However, now I need to create a surface for Vulkan device and here I hit troubles.

As I got to create a surface on macOS, I need to call vkCreateMacOSSurfaceMVK() (ref) from #include <vulkan/vulkan_macos.h> but when I try to include this header my app crashes with errors:

[build] /Projects/my/third-party/vulkan/include/vulkan/vulkan_macos.h:26:9: error: unknown type name 'VkFlags'
[build] typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
[build]         ^
[build] /Projects/my/third-party/vulkan/include/vulkan/vulkan_macos.h:28:5: error: unknown type name 'VkStructureType'
[build]     VkStructureType                 sType;
[build]     ^
[build] /Projects/my/third-party/vulkan/include/vulkan/vulkan_macos.h:34:19: error: unknown type name 'VKAPI_PTR'
[build] typedef VkResult (VKAPI_PTR *PFN_vkCreateMacOSSurfaceMVK)(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
[build]                   ^
[build] /Projects/my/third-party/vulkan/include/vulkan/vulkan_macos.h:34:59: error: unknown type name 'VkInstance'
[build] typedef VkResult (VKAPI_PTR *PFN_vkCreateMacOSSurfaceMVK)(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);

And if to open this header there are really no declarations or other includes for these types:

#ifndef VULKAN_MACOS_H_
#define VULKAN_MACOS_H_ 1

/*
** Copyright 2015-2024 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
*/

/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/


#ifdef __cplusplus
extern "C" {
#endif



// VK_MVK_macos_surface is a preprocessor guard. Do not pass it to API calls.
#define VK_MVK_macos_surface 1
#define VK_MVK_MACOS_SURFACE_SPEC_VERSION 3
#define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
typedef struct VkMacOSSurfaceCreateInfoMVK {
    VkStructureType                 sType;
    const void*                     pNext;
    VkMacOSSurfaceCreateFlagsMVK    flags;
    const void*                     pView;
} VkMacOSSurfaceCreateInfoMVK;

typedef VkResult (VKAPI_PTR *PFN_vkCreateMacOSSurfaceMVK)(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);

#ifndef VK_NO_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkCreateMacOSSurfaceMVK(
    VkInstance                                  instance,
    const VkMacOSSurfaceCreateInfoMVK*          pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkSurfaceKHR*                               pSurface);
#endif

#ifdef __cplusplus
}
#endif

#endif

I found yet one header <vulkan/vulkan_metal.h> but it didn't help too.

What's a right way to create a Metal surface for Vulkan on macOS?

P.S. I know there are GLFW and similar libraries but I just want to dig into clean Cocoa and Vulkan (MoltenVK) to understand how to connect them manually.

P.P.S. I am not sure if it's right to call AppKit, MetalKit and other frameworks a part of Cocoa, fix me if I am wrong.

3

There are 3 best solutions below

0
On BEST ANSWER

This is the combination of the two following questions and their answers (one of them being mine):

TL;DR:

  • As for all Vulkan extensions, function pointers must be loaded dynamically using vkGetInstanceProcAddr or vkGetDeviceProcAddr. Note that you will need to specify the corresponding extensions upon instance creation in VkInstanceCreateInfo::ppEnabledExtensionNames. You can also use some libraries like Volk that exist for this purpose.

  • Headers such as vulkan_metal.h or vulkan_win32.h and their siblings require the contents of vulkan_core.h, that must be included before it. Though the vulkan.h header does this for you (provided you added the correct #define VK_USE_PLATFORM_XXX), it also #includes potentially-heavy platform-specific headers, such as <windows.h> on Windows, or <X11/Xlib.h> on Linux with X11. This isn't good for compile times. As far as I know, a few forward-declaration suffice, so you may build your own vulkan.h that may look like this instead:

#ifndef YOUR_VULKAN_H
#define YOUR_VULKAN_H

#include <vulkan/vulkan_core.h>

#if defined(VK_USE_PLATFORM_XXXX)

// Some forward declarations

#include <vulkan/the_platform_header.h>

#endif // #if defined(VK_USE_PLATFORM_XXXX)

#endif // YOUR_VULKAN_H

Note: this is what Volk does.

0
On

Researching a GLFW's source code I've finally got how to call vkCreateMacOSSurfaceMVK(...).

  1. We need to turn on one from two extensions (it depends on version):

     #define VK_EXT_METAL_SURFACE_NAME "VK_EXT_metal_surface"
     #define VK_MVK_MACOS_SURFACE_NAME "VK_MVK_macos_surface"
    
     std::vector<const char*> ext_names = {
         VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
         VK_EXT_METAL_SURFACE_NAME,
         VK_MVK_MACOS_SURFACE_NAME,
         // VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
     };
    
     // initialize the VkInstanceCreateInfo structure
     VkInstanceCreateInfo inst_info = {};
     inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
     inst_info.pNext = NULL;
     inst_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
     inst_info.pApplicationInfo = &app_info;
     inst_info.enabledExtensionCount = static_cast<uint32_t>(ext_names.size());;
     inst_info.ppEnabledExtensionNames = ext_names.data();
     inst_info.enabledLayerCount = 0;
     inst_info.ppEnabledLayerNames = NULL;
    
  2. Need to define prototypes for functions from these extensions:

     // A little changed code of GLFW
     typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
     typedef VkFlags VkMetalSurfaceCreateFlagsEXT;
    
     typedef struct VkMacOSSurfaceCreateInfoMVK
     {
         VkStructureType                 sType;
         const void*                     pNext;
         VkMacOSSurfaceCreateFlagsMVK    flags;
         const void*                     pView;
     } VkMacOSSurfaceCreateInfoMVK;
    
     typedef struct VkMetalSurfaceCreateInfoEXT
     {
         VkStructureType                 sType;
         const void*                     pNext;
         VkMetalSurfaceCreateFlagsEXT    flags;
         const void*                     pLayer;
     } VkMetalSurfaceCreateInfoEXT;
    
     typedef VkResult (*PFN_vkCreateMacOSSurfaceMVK)(VkInstance, const VkMacOSSurfaceCreateInfoMVK*, const VkAllocationCallbacks*, VkSurfaceKHR*);
     typedef VkResult (*PFN_vkCreateMetalSurfaceEXT)(VkInstance, const VkMetalSurfaceCreateInfoEXT*, const VkAllocationCallbacks*, VkSurfaceKHR*);
    
  3. As a third step, we need to find an address of any one from these functions:

     #define VK_CREATE_MACOS_SURFACE_MVK "vkCreateMacOSSurfaceMVK"
     #define VK_CREATE_METAL_SURFACE_EXT "vkCreateMetalSurfaceEXT"
    
     // In my case it's `vkCreateMacOSSurfaceMVK`, so I will call this variant
     auto address = (PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr(instance, VK_CREATE_MACOS_SURFACE_MVK);
    
  4. Profit!

P.S. All code above it's mostly a copy-pasta from GLFW, so you can always learn more about it researching GLFW's source code. Unfortunately, I am a noobie in C++ and if you are too, I hope this code fragment will save several hours of your time and few cups of coffee.

UPD

I've suddenly realised that vulkan_metal.h and vulkan_macos.h are build-in interface implementations for these extensions.

0
On

you may just need define a macro VK_USE_PLATFORM_MACOS_MVK before include <vulkan/vulkan.h>, then <vulkan/vulkan_macos.h> will be include automatically.

note: it's recommended to define VK_USE_PLATFORM_METAL_EXT witch support both ios and macos, instead of VK_USE_PLATFORM_IOS_MVK or VK_USE_PLATFORM_MACOS_MVK.