From e0ed0ea2f3cf5a6b5d4ce07d7a1aac820eee2ce7 Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Fri, 20 May 2022 11:50:42 -0400
Subject: [PATCH] vulkan(Windows): verify vulkan instance creation with
 vulkan-1.dll

Add another supplementary check using functions from the vulkan-1
module before trying with QVulkanInstance.

Gitlab: #746
Change-Id: I8975ef8765675aca95c4fee648853ea74c0c8ec9
---
 src/app/main.cpp  | 15 +++++++++++++++
 src/app/utils.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++
 src/app/utils.h   |  3 +++
 3 files changed, 63 insertions(+)

diff --git a/src/app/main.cpp b/src/app/main.cpp
index bf56278a3..69abdd5cb 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -20,6 +20,7 @@
 
 #include "mainapplication.h"
 #include "instancemanager.h"
+#include "utils.h"
 #include "version.h"
 
 #include <QCryptographicHash>
@@ -117,6 +118,19 @@ main(int argc, char* argv[])
 #else
     if (std::invoke([] {
 #if defined(HAS_VULKAN) && !defined(Q_OS_LINUX)
+            // Somehow, several bug reports show that, on Windows, QVulkanInstance
+            // verification  passes, but goes on to fail when creating the QQuickWindow
+            // with "Failed to initialize graphics backend for Vulkan".
+            // Here we allow platform-specific checks using native Vulkan libraries.
+            // Currently only implemented on Windows.
+            try {
+                Utils::testVulkanSupport();
+            } catch (const std::exception& e) {
+                qWarning() << "Vulkan instance cannot be created:" << e.what();
+                return false;
+            }
+
+            // Check using Qt's QVulkanInstance.
             QVulkanInstance inst;
             inst.setLayers({"VK_LAYER_KHRONOS_validation"});
             bool ok = inst.create();
@@ -128,6 +142,7 @@ main(int argc, char* argv[])
                 qWarning() << "VK_LAYER_KHRONOS_validation layer is not available.";
                 return false;
             }
+
             return true;
 #else
             return false;
diff --git a/src/app/utils.cpp b/src/app/utils.cpp
index 2114f9b0e..5422c4278 100644
--- a/src/app/utils.cpp
+++ b/src/app/utils.cpp
@@ -51,6 +51,51 @@
 #include <windows.h>
 #endif
 
+void
+Utils::testVulkanSupport()
+{
+#if defined(Q_OS_WIN)
+    // Checks Vulkan support using the vulkan functions loaded directly
+    // from vulkan-1.dll.
+    struct DllLoader
+    {
+        explicit DllLoader(const std::string& filename)
+            : module(LoadLibraryA(filename.c_str()))
+        {
+            if (module == nullptr) {
+                throw std::runtime_error("Can't load module.");
+            }
+        }
+        ~DllLoader()
+        {
+            FreeLibrary(module);
+        }
+        HMODULE module;
+    } vk {"vulkan-1.dll"};
+
+    typedef void*(__stdcall * PFN_vkGetInstanceProcAddr)(void*, const char*);
+    auto vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)
+        GetProcAddress(vk.module, "vkGetInstanceProcAddr");
+    if (!vkGetInstanceProcAddr) {
+        throw std::runtime_error("Missing vkGetInstanceProcAddr proc.");
+    }
+
+    typedef int(__stdcall * PFN_vkCreateInstance)(int*, void*, void**);
+    auto vkCreateInstance = (PFN_vkCreateInstance) vkGetInstanceProcAddr(vk.module,
+                                                                         "vkCreateInstance");
+    if (!vkCreateInstance) {
+        throw std::runtime_error("Missing vkCreateInstance proc.");
+    }
+
+    void* instance = 0;
+    int VkInstanceCreateInfo[16] = {1};
+    auto result = vkCreateInstance(VkInstanceCreateInfo, 0, &instance);
+    if (!instance || result != 0) {
+        throw std::runtime_error("Can't create Vulkan instance.");
+    }
+#endif
+}
+
 bool
 Utils::CreateStartupLink(const std::wstring& wstrAppName)
 {
diff --git a/src/app/utils.h b/src/app/utils.h
index a4e6ac8db..35820c0b1 100644
--- a/src/app/utils.h
+++ b/src/app/utils.h
@@ -56,6 +56,9 @@ class LRCInstance;
 
 namespace Utils {
 
+// Throws if Vulkan cannot be instantiated.
+void testVulkanSupport();
+
 // App System
 bool CreateStartupLink(const std::wstring& wstrAppName);
 void DeleteStartupLink(const std::wstring& wstrAppName);
-- 
GitLab