Glusoft

Create a simple example with Vulkan and MoltenVK with Cmake on MacOs

The first thing to do is to have cmake:

brew install cmake

After that you can install molten-vk:

brew install molten-vk
You need to have a simple CmakeLists.txt for you project, using SDL2 and moltenvk:
cmake_minimum_required(VERSION 3.10)

project(SDL2Vulkan LANGUAGES CXX)

# Find the libraries
find_package(SDL2 REQUIRED)

# Find the MoltenVK libraries
find_path(MOLTENVK_INCLUDE_DIR vulkan/vulkan.h HINTS "/usr/local/include" "/usr/local/opt/molten-vk/include")
find_library(MOLTENVK_LIB NAMES MoltenVK PATHS "/usr/local/lib" "/usr/local/opt/molten-vk/lib")

if(NOT MOLTENVK_INCLUDE_DIR OR NOT MOLTENVK_LIB)
    message(FATAL_ERROR "Could not find MoltenVK. Please make sure it is installed.")
endif()

# Set up Vulkan include directories and link libraries
include_directories(${SDL2_INCLUDE_DIRS} ${MOLTENVK_INCLUDE_DIR})

# Create an executable target
add_executable(${PROJECT_NAME} main.cpp)

# Link the libraries to the target
target_link_libraries(${PROJECT_NAME} 
                        "-framework QuartzCore" 
                        "-framework Metal" 
                        "-framework Foundation" 
                        "-framework IOSurface" 
                        "-framework CoreGraphics" 
                        "-framework IOKit" 
                        "-framework AppKit" 
                        SDL2::SDL2 
                        SDL2::SDL2main 
                        ${MOLTENVK_LIB}
)

The main.cpp simply test if vulkan is setting things up correcty, you should Have nothing diplayed on the screen but no errors in the terminal

#include <iostream>
#include <vector>
#include <vulkan/vulkan.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#ifndef UINT32_MAX
    #define UINT32_MAX 0xffffffff
#endif

void check_vk_result(VkResult result, const char* operation) {
    if (result != VK_SUCCESS) {
        std::cerr << "Vulkan error (" << operation << "): " << result << std::endl;
        exit(1);
    }
}

int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        std::cerr << "SDL_Init Error: " << SDL_GetError() << std::endl;
        return 1;
    }
    if (SDL_Vulkan_LoadLibrary(nullptr) != 0) {
        std::cerr << "SDL_Vulkan_LoadLibrary Error: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }
    
    SDL_Window* window = SDL_CreateWindow("SDL2Vulkan", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 360, SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN);
    
    if (window == nullptr) {
        std::cerr << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }

    uint32_t extensionCount;
    const char** extensionNames = 0;
    if (!SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr)) {
        std::cerr << "SDL_Vulkan_GetInstanceExtensions Error: " << SDL_GetError() << std::endl;
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    extensionNames = new const char *[extensionCount];
    if (!SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensionNames)) {
        std::cerr << "SDL_Vulkan_GetInstanceExtensions Error: " << SDL_GetError() << std::endl;
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }
    
    const VkInstanceCreateInfo instInfo = {
        VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType
        nullptr,                                // pNext
        0,                                      // flags
        nullptr,                                // pApplicationInfo
        0,                                      // enabledLayerCount
        nullptr,                                // ppEnabledLayerNames
        extensionCount,                         // enabledExtensionCount
        extensionNames,                         // ppEnabledExtensionNames
    };
    VkInstance vkInst;
    VkResult result = vkCreateInstance(&instInfo, nullptr, &vkInst);
    check_vk_result(result, "vkCreateInstance");

    uint32_t physicalDeviceCount;
    result = vkEnumeratePhysicalDevices(vkInst, &physicalDeviceCount, nullptr);

    check_vk_result(result, "vkEnumeratePhysicalDevices");

    if (physicalDeviceCount == 0) {
        std::cerr << "Failed to find GPUs with Vulkan support!" << std::endl;
        vkDestroyInstance(vkInst, nullptr);
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
    vkEnumeratePhysicalDevices(vkInst, &physicalDeviceCount, physicalDevices.data());
    VkPhysicalDevice physicalDevice = physicalDevices[0];

    uint32_t queueFamilyCount;
    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
    std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());

    VkSurfaceKHR surface;
    if (!SDL_Vulkan_CreateSurface(window, vkInst, &surface)) {
        std::cerr << "SDL_Vulkan_CreateSurface Error: " << SDL_GetError() << std::endl;
        vkDestroyInstance(vkInst, nullptr);
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    uint32_t graphicsQueueIndex = UINT32_MAX;
    uint32_t presentQueueIndex = UINT32_MAX;
    VkBool32 support;
    uint32_t i = 0;
    for (VkQueueFamilyProperties queueFamily : queueFamilies) {
        if (graphicsQueueIndex == UINT32_MAX && queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
            graphicsQueueIndex = i;
        if (presentQueueIndex == UINT32_MAX) {
            result = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &support);
            check_vk_result(result, "vkGetPhysicalDeviceSurfaceSupportKHR");
            if(support)
                presentQueueIndex = i;
        }
        ++i;
    }

    float queuePriority = 1.0f;
    VkDeviceQueueCreateInfo queueInfo = {
        VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
        nullptr,                                    // pNext
        0,                                          // flags
        graphicsQueueIndex,                         // graphicsQueueIndex
        1,                                          // queueCount
        &queuePriority,                             // pQueuePriorities
    };
    
    VkPhysicalDeviceFeatures deviceFeatures = {};
    const char* deviceExtensionNames[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
    VkDeviceCreateInfo createInfo = {
        VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,   // sType
        nullptr,                                // pNext
        0,                                      // flags
        1,                                      // queueCreateInfoCount
        &queueInfo,                             // pQueueCreateInfos
        0,                                      // enabledLayerCount
        nullptr,                                // ppEnabledLayerNames
        1,                                      // enabledExtensionCount
        deviceExtensionNames,                   // ppEnabledExtensionNames
        &deviceFeatures,                        // pEnabledFeatures
    };
    VkDevice device;
    result = vkCreateDevice(physicalDevice, &createInfo, nullptr, &device);
    check_vk_result(result, "vkCreateDevice");

    VkQueue graphicsQueue;
    vkGetDeviceQueue(device, graphicsQueueIndex, 0, &graphicsQueue);

    VkQueue presentQueue;
    vkGetDeviceQueue(device, presentQueueIndex, 0, &presentQueue);

    SDL_Log("Initialized successfully");
    
    bool running = true;
    while(running) {
        SDL_Event windowEvent;
        while(SDL_PollEvent(&windowEvent))
            if(windowEvent.type == SDL_QUIT) {
                running = false;
                break;
            }
    }

    vkDestroyDevice(device, nullptr);
    vkDestroyInstance(vkInst, nullptr);
    SDL_DestroyWindow(window);
    SDL_Vulkan_UnloadLibrary();
    SDL_Quit();

    return 0;
}