KhronosGroup / MoltenVK

MoltenVK is a Vulkan Portability implementation. It layers a subset of the high-performance, industry-standard Vulkan graphics and compute API over Apple's Metal graphics framework, enabling Vulkan applications to run on macOS, iOS and tvOS.
Apache License 2.0
4.85k stars 429 forks source link

Calling vkCreateSwapchainKHR on non-main thread cause stuck #2357

Open XieYHccc opened 2 months ago

XieYHccc commented 2 months ago

Hi everyone,

I'm using a Job system to split the initialization of my engine into different jobs. The problem is that calling vkCreateSwapchainKHR on a non-main thread mysteriously hangs with no validation errors(The same code works on Windows, see below). However, when I move the initialization of Vulkan to main thread, it works.

So, Is there any thing I missed? or should we must call vkCreateSwapchainKHR on main thread on Macos?

Application::Application(const ApplicationSpecification& specs) 
{
    s_Instance = this;

    Logger::Init();

    // Init Job System
    m_JobSystem = CreateScope<JobSystem>();

    // Init Event Manager
    EventManager::CreateSingleton();

    // Init Input System
    Input::CreateSingleton();
    Input::Get()->Init();

    // Create Window
    {
        WindowSpecification windowSpec;
        windowSpec.width = specs.width;
        windowSpec.height = specs.height;
        windowSpec.title = specs.title;
        windowSpec.is_fullscreen = specs.isFullScreen;

#if defined(QK_PLATFORM_WINDOWS) || defined(QK_PLATFORM_MACOS)
        m_Window = CreateScope<WindowGLFW>(windowSpec);
        m_Window->Init();
#endif
        NFD::Init();

        QK_CORE_LOGI_TAG("Core", "Window created");
    }

    // Init Graphic Device and Renderer
    JobSystem::Counter counter;
    m_JobSystem->Execute([this]()
    {
#ifdef USE_VULKAN_DRIVER
        m_GraphicDevice = CreateScope<graphic::Device_Vulkan>();
        m_GraphicDevice->Init();
#endif
        GpuResourceManager::CreateSingleton();
        GpuResourceManager::Get().Init();
    }, &counter);

    // Init Asset system
    m_JobSystem->Execute([this, &counter]() 
    {
        m_JobSystem->Wait(&counter, 1);
        AssetManager::CreateSingleton(); 
    });

    // Init UI system
    m_JobSystem->Execute([this, &specs, &counter]() 
    {
        m_JobSystem->Wait(&counter, 1);
        UI::CreateSingleton();
        UI::Get()->Init(m_GraphicDevice.get(), specs.uiSpecs);
    });

    m_JobSystem->Wait(&counter, 1);

    // Register application callback functions
    EventManager::Get().Subscribe<WindowCloseEvent>([this](const WindowCloseEvent& event) { OnWindowClose(event);});
    EventManager::Get().Subscribe<WindowResizeEvent>([this](const WindowResizeEvent& event) { OnWindowResize(event); });
}
AlexanderDevaikinEnscape commented 1 month ago

AFAIK it's because macOS requires interacting with UI only on main thread, and swapchain creation in MoltenVK is UI surface interaction on the Metal/OS level.

billhollings commented 1 month ago

AFAIK it's because macOS requires interacting with UI only on main thread, and swapchain creation in MoltenVK is UI surface interaction on the Metal/OS level.

Yes. In the Apple APIs, access to the main UI components, such as screens or views, which MoltenVK may need to do during swapchain creation, must be done on the main thread.

If swapchain creation is done from a secondary thread, MoltenVK dispatches any necessary UI calls to the main thread, and waits for them to complete. If the app is also waiting on the main thread for the swapchain to be created, this can cause a deadlock.

@XieYHccc

Are you able to generate a call stack trace of the hang, so we can see where it is hanging?

If you're running the app from Xcode, you can pause the app and screenshot the call stacks.