vkcube: Improve GPU auto-selection

Instead of selecting GPU 0, prefer dGPU, iGPU, vGPU, CPU, OTHER.
In that order.

If user selects specific gpu_number (including 0), use only
that and don't fallback to anything else, even when incorrect.
diff --git a/cube/cube.c b/cube/cube.c
index 9516fca..800a069 100644
--- a/cube/cube.c
+++ b/cube/cube.c
@@ -343,7 +343,7 @@
     bool use_staging_buffer;
     bool separate_present_queue;
     bool is_minimized;
-    uint32_t gpu_number;
+    int32_t gpu_number;
 
     bool VK_KHR_incremental_present_enabled;
 
@@ -3248,8 +3248,6 @@
         inst_info.pNext = &dbg_messenger_create_info;
     }
 
-    uint32_t gpu_count;
-
     err = vkCreateInstance(&inst_info, NULL, &demo->inst);
     if (err == VK_ERROR_INCOMPATIBLE_DRIVER) {
         ERR_EXIT(
@@ -3269,22 +3267,12 @@
             "vkCreateInstance Failure");
     }
 
-    /* Make initial call to query gpu_count, then second call for gpu info*/
+    /* Make initial call to query gpu_count, then second call for gpu info */
+    uint32_t gpu_count = 0;
     err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL);
     assert(!err);
 
-    if (gpu_count > 0) {
-        VkPhysicalDevice *physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count);
-        err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, physical_devices);
-        assert(!err);
-        if (demo->gpu_number > gpu_count - 1) {
-            fprintf(stderr, "Gpu %u specified is not present, gpu count = %u\n", demo->gpu_number, gpu_count);
-            fprintf(stderr, "Continuing with gpu 0\n");
-            demo->gpu_number = 0;
-        }
-        demo->gpu = physical_devices[demo->gpu_number];
-        free(physical_devices);
-    } else {
+    if (gpu_count <= 0) {
         ERR_EXIT(
             "vkEnumeratePhysicalDevices reported zero accessible devices.\n\n"
             "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
@@ -3292,6 +3280,57 @@
             "vkEnumeratePhysicalDevices Failure");
     }
 
+    VkPhysicalDevice *physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count);
+    err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, physical_devices);
+    assert(!err);
+    if (demo->gpu_number >= 0 && !((uint32_t)demo->gpu_number < gpu_count)) {
+        fprintf(stderr, "GPU %d specified is not present, GPU count = %u\n", demo->gpu_number, gpu_count);
+        ERR_EXIT("Specified GPU number is not present", "User Error");
+    }
+
+    /* Try to auto select most suitable device */
+    if (demo->gpu_number == -1) {
+        uint32_t count_device_type[VK_PHYSICAL_DEVICE_TYPE_CPU + 1];
+        memset(count_device_type, 0, sizeof(count_device_type));
+
+        VkPhysicalDeviceProperties physicalDeviceProperties;
+        for (uint32_t i = 0; i < gpu_count; i++) {
+            vkGetPhysicalDeviceProperties(physical_devices[i], &physicalDeviceProperties);
+            assert(physicalDeviceProperties.deviceType <= VK_PHYSICAL_DEVICE_TYPE_CPU);
+            count_device_type[physicalDeviceProperties.deviceType]++;
+        }
+
+        VkPhysicalDeviceType search_for_device_type = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
+        if (count_device_type[VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU]) {
+            search_for_device_type = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
+        } else if (count_device_type[VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU]) {
+            search_for_device_type = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
+        } else if (count_device_type[VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU]) {
+            search_for_device_type = VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU;
+        } else if (count_device_type[VK_PHYSICAL_DEVICE_TYPE_CPU]) {
+            search_for_device_type = VK_PHYSICAL_DEVICE_TYPE_CPU;
+        } else if (count_device_type[VK_PHYSICAL_DEVICE_TYPE_OTHER]) {
+            search_for_device_type = VK_PHYSICAL_DEVICE_TYPE_OTHER;
+        }
+
+        for (uint32_t i = 0; i < gpu_count; i++) {
+            vkGetPhysicalDeviceProperties(physical_devices[i], &physicalDeviceProperties);
+            if (physicalDeviceProperties.deviceType == search_for_device_type) {
+                demo->gpu_number = i;
+                break;
+            }
+        }
+    }
+    assert(demo->gpu_number >= 0);
+    demo->gpu = physical_devices[demo->gpu_number];
+    {
+        VkPhysicalDeviceProperties physicalDeviceProperties;
+        vkGetPhysicalDeviceProperties(demo->gpu, &physicalDeviceProperties);
+        fprintf(stderr, "Selected GPU %d: %s, type: %u\n", demo->gpu_number, physicalDeviceProperties.deviceName,
+                physicalDeviceProperties.deviceType);
+    }
+    free(physical_devices);
+
     /* Look for device extensions */
     uint32_t device_extension_count = 0;
     VkBool32 swapchainExtFound = 0;
@@ -3816,8 +3855,8 @@
     memset(demo, 0, sizeof(*demo));
     demo->presentMode = VK_PRESENT_MODE_FIFO_KHR;
     demo->frameCount = INT32_MAX;
-    /* For cube demo we just grab the first physical device by default */
-    demo->gpu_number = 0;
+    /* Autodetect suitable / best GPU by default */
+    demo->gpu_number = -1;
 
     for (int i = 1; i < argc; i++) {
         if (strcmp(argv[i], "--use_staging") == 0) {
@@ -3865,6 +3904,7 @@
         }
         if ((strcmp(argv[i], "--gpu_number") == 0) && (i < argc - 1)) {
             demo->gpu_number = atoi(argv[i + 1]);
+            assert(demo->gpu_number >= 0);
             i++;
             continue;
         }
diff --git a/cube/cube.cpp b/cube/cube.cpp
index 252f880..8efea2a 100644
--- a/cube/cube.cpp
+++ b/cube/cube.cpp
@@ -311,7 +311,7 @@
     bool use_staging_buffer;
     bool use_xlib;
     bool separate_present_queue;
-    uint32_t gpu_number;
+    int32_t gpu_number;
 
     vk::Instance inst;
     vk::PhysicalDevice gpu;
@@ -940,8 +940,8 @@
     presentMode = vk::PresentModeKHR::eFifo;
     frameCount = UINT32_MAX;
     use_xlib = false;
-    /* For cube demo we just grab the first physical device by default */
-    gpu_number = 0;
+    /* Autodetect suitable / best GPU by default */
+    gpu_number = -1;
 
     for (int i = 1; i < argc; i++) {
         if (strcmp(argv[i], "--use_staging") == 0) {
@@ -976,6 +976,7 @@
         }
         if ((strcmp(argv[i], "--gpu_number") == 0) && (i < argc - 1)) {
             gpu_number = atoi(argv[i + 1]);
+            assert(gpu_number >= 0);
             i++;
             continue;
         }
@@ -988,7 +989,7 @@
               << "\t\tVK_PRESENT_MODE_IMMEDIATE_KHR = " << VK_PRESENT_MODE_IMMEDIATE_KHR << "\n"
               << "\t\tVK_PRESENT_MODE_MAILBOX_KHR = " << VK_PRESENT_MODE_MAILBOX_KHR << "\n"
               << "\t\tVK_PRESENT_MODE_FIFO_KHR = " << VK_PRESENT_MODE_FIFO_KHR << "\n"
-              << "\t\tVK_PRESENT_MODE_FIFO_RELAXED_KHR = " << VK_PRESENT_MODE_FIFO_RELAXED_KHR;
+              << "\t\tVK_PRESENT_MODE_FIFO_RELAXED_KHR = " << VK_PRESENT_MODE_FIFO_RELAXED_KHR << "\n";
 
 #if defined(_WIN32)
         if (!suppress_popups) MessageBox(NULL, usage.str().c_str(), "Usage Error", MB_OK);
@@ -1243,22 +1244,12 @@
             "vkCreateInstance Failure");
     }
 
-    /* Make initial call to query gpu_count, then second call for gpu info*/
-    uint32_t gpu_count;
+    /* Make initial call to query gpu_count, then second call for gpu info */
+    uint32_t gpu_count = 0;
     result = inst.enumeratePhysicalDevices(&gpu_count, static_cast<vk::PhysicalDevice *>(nullptr));
     VERIFY(result == vk::Result::eSuccess);
 
-    if (gpu_count > 0) {
-        std::unique_ptr<vk::PhysicalDevice[]> physical_devices(new vk::PhysicalDevice[gpu_count]);
-        result = inst.enumeratePhysicalDevices(&gpu_count, physical_devices.get());
-        VERIFY(result == vk::Result::eSuccess);
-        if (gpu_number > gpu_count - 1) {
-            fprintf(stderr, "Gpu %u specified is not present, gpu count = %u\n", gpu_number, gpu_count);
-            fprintf(stderr, "Continuing with gpu 0\n");
-            gpu_number = 0;
-        }
-        gpu = physical_devices[gpu_number];
-    } else {
+    if (gpu_count <= 0) {
         ERR_EXIT(
             "vkEnumeratePhysicalDevices reported zero accessible devices.\n\n"
             "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
@@ -1266,6 +1257,54 @@
             "vkEnumeratePhysicalDevices Failure");
     }
 
+    std::unique_ptr<vk::PhysicalDevice[]> physical_devices(new vk::PhysicalDevice[gpu_count]);
+    result = inst.enumeratePhysicalDevices(&gpu_count, physical_devices.get());
+    VERIFY(result == vk::Result::eSuccess);
+
+    if (gpu_number >= 0 && !((uint32_t)gpu_number < gpu_count)) {
+        fprintf(stderr, "GPU %d specified is not present, GPU count = %u\n", gpu_number, gpu_count);
+        ERR_EXIT("Specified GPU number is not present", "User Error");
+    }
+
+    /* Try to auto select most suitable device */
+    if (gpu_number == -1) {
+        uint32_t count_device_type[VK_PHYSICAL_DEVICE_TYPE_CPU + 1];
+        memset(count_device_type, 0, sizeof(count_device_type));
+
+        for (uint32_t i = 0; i < gpu_count; i++) {
+            const auto physicalDeviceProperties = physical_devices[i].getProperties();
+            assert(static_cast<int>(physicalDeviceProperties.deviceType) <= VK_PHYSICAL_DEVICE_TYPE_CPU);
+            count_device_type[static_cast<int>(physicalDeviceProperties.deviceType)]++;
+        }
+
+        const vk::PhysicalDeviceType device_type_preference[] = {
+            vk::PhysicalDeviceType::eDiscreteGpu, vk::PhysicalDeviceType::eIntegratedGpu, vk::PhysicalDeviceType::eVirtualGpu,
+            vk::PhysicalDeviceType::eCpu, vk::PhysicalDeviceType::eOther};
+        vk::PhysicalDeviceType search_for_device_type = vk::PhysicalDeviceType::eDiscreteGpu;
+        for (uint32_t i = 0; i < sizeof(device_type_preference) / sizeof(vk::PhysicalDeviceType); i++) {
+            if (count_device_type[static_cast<int>(device_type_preference[i])]) {
+                search_for_device_type = device_type_preference[i];
+                break;
+            }
+        }
+
+        for (uint32_t i = 0; i < gpu_count; i++) {
+            const auto physicalDeviceProperties = physical_devices[i].getProperties();
+            if (physicalDeviceProperties.deviceType == search_for_device_type) {
+                gpu_number = i;
+                break;
+            }
+        }
+    }
+    assert(gpu_number >= 0);
+    gpu = physical_devices[gpu_number];
+    {
+        auto physicalDeviceProperties = gpu.getProperties();
+        fprintf(stderr, "Selected GPU %d: %s, type: %s\n", gpu_number, physicalDeviceProperties.deviceName.data(),
+                to_string(physicalDeviceProperties.deviceType).c_str());
+    }
+    physical_devices.reset();
+
     /* Look for device extensions */
     uint32_t device_extension_count = 0;
     vk::Bool32 swapchainExtFound = VK_FALSE;