Search code examples
visual-studio-codecmakeapple-m1vulkan

creating a Vulkan Instance in version 1.3.224 doesn't work and results in an Error VK_ERROR_INCOMPATIBLE_DRIVER on Apple M1


Hello I am new to Vulkan and c++.

I am following a tutorial on Vulkan for Visual-Studio and stuck at creating a Vulkan Instance. Through a lot of googling I found out that the error -9 (VK_ERROR_INCOMPATIBLE_DRIVER) is based on the Vulkan Version 1.3, me using MacOS and how it implements the MoltenVk or must be included or something else like that. There it got too complicated for me.

I have tried to search through the documentations and now I don't really even understand what MoltenVk is or how I even tried to include Vulkan with cmake in my project.

I may found a solution on [here] and on stackoverflow (Why does vkCreateInstance return "VK_ERROR_INCOMPATIBLE_DRIVER" on MacOS despite vulkan supported GPU and up to date driver?) but I am not able to implement/understand it. Maybe somebody can explain it or tell me how to implement it?. The Site said I should put something like that in

std::vector<const char*>

extNames.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);

extNames.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);


VkInstanceCreateInfo inst_info = {};

ins_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;

inst_info.enabledExtensionCount = static_cast<uint32_t>(extNames.size());

inst_info.ppEnabledExtensionNames = extNames.data();

I have tried it that way and with some deviations in the vk_device.cpp file without any success.

My Project basically looks like this:

Project
--.vscode
--bin
  ->Project.exe
--build
--CMakeFiles
--libs
  --SDL
--src
  --Vk_base
    ->vk_base.h
    ->vk_device.cpp
  ->main.cpp
->CMakeList.txt

vk_base.h:

    #include <vulkan/vulkan.h>
    struct VulkanContext {
    VkInstance instance;
    }; 
    VulkanContext* initVulkan();

vk_device.cpp:

#include "vk_base.h"
#include <iostream>

bool initVulkanInstance(VulkanContext* context) {
    
    VkApplicationInfo appInfo = {};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "Vulkan App";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_VERSION_1_0;

    VkInstanceCreateInfo createInfo = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
    createInfo.pApplicationInfo = &appInfo;
    createInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; // MAC OS SPECIFIC
    //createInfo.enabledExtensionCount = static_cast<uint32_t>(extNames.size()); // MAC OS SPECIFIC
    //createInfo.ppEnabledExtensionNames = extNames.data(); // MAC OS SPECIFIC

    VkResult creation_result = vkCreateInstance(&createInfo, 0, &context->instance);
    std::cout << creation_result << std::endl;
    return true;
}

VulkanContext* initVulkan() {
    VulkanContext* context = new VulkanContext();
    if (initVulkanInstance(context) != VK_SUCCESS) {
        throw std::runtime_error("failed to create instance!");
        
    }

    return context;
}


main.cpp

#include <iostream>
#include <SDL.h>
#include "vk_base/vk_base.h"


bool handleMessage() {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        switch (event.type) {
            case SDL_QUIT:
                return false;
            case SDL_KEYDOWN:
                if (event.key.keysym.sym == SDLK_ESCAPE) {
                    return false;
                }
                break;
        }
    }

}

int main() {

    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window *_window;
    _window = SDL_CreateWindow("Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 500, SDL_WINDOW_RESIZABLE);

    SDL_Event e;
    bool quit = false;
    while (!quit) {
        while(SDL_PollEvent(&e) != 0){
            if(e.type == SDL_QUIT){
                quit = true;
            }
        }
    }

    VulkanContext* context = initVulkan();

    SDL_DestroyWindow(_window);
    SDL_Quit();

return 0; 

}

and my Cmakefile to create the Project in the build folder with the command : cmake ../ -GXcode ( I have tried it with -GXcode and without)

CMakeLists.txt:


#project name
project(Project)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin")
set(SOURCE_FILES src/main.cpp src/vk_base/vk_device.cpp)
#find SDL2
add_subdirectory(libs/SDL)


#find Vulkan

find_package(Vulkan REQUIRED)

#vulkan executebl
add_executable(Project ${SOURCE_FILES})
target_include_directories(Project PUBLIC libs/SDL/include)
target_link_libraries(Project PUBLIC SDL2-static)
target_link_libraries(Project PUBLIC Vulkan::Vulkan)
target_link_libraries(Project PUBLIC ${Vulkan_LIBRARIES})

Thank you for reading and sry for my bad english:) if there are misspellings in it, it is probably not the cause of the problem and just a typo from me on this thread :p


Solution

  • if you are not comfortable with C++, steer away from Vulkan. One will teach you exactly the wrong lessons about the other. Vulkan is low-level API, and shows you how you should not be programming C++ in 98 % of the time.

    Apple does not support Vulkan. They have their own API Metal. MoltenVK is a library that attempts to translate Vulkan function calls to Metal calls.

    This API translation does not match 1:1, so the MoltenVK wrapper is not a conformant Vulkan implementation. You need to waive that it is not conformant, and that you know of and will avoid triggering the limitations:

    You waive the Vulkan conformance by adding VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR bit to the Instance creation:

    #ifdef __APPLE__
    createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
    #endif //__APPLE__
    

    VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR is part of a Vulkan extension, so that extension needs to be enabled before using the bit, per standard Vulkan rules:

    #ifdef __APPLE__
    extNames.push_back( VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME );
    #endif //__APPLE__
    
    createInfo.enabledExtensionCount = static_cast<uint32_t>(extNames.size());
    createInfo.ppEnabledExtensionNames = extNames.data();