Added parsing of command line parameters and GPU selection

Command line syntax:
-h, --Help   Print this information
-l, --List   Print list of GPUs
-g S, --GPU S   Select GPU with name containing S
-i N, --GPUIndex N   Select GPU index N

Also improved error handling.
diff --git a/src/Common.cpp b/src/Common.cpp
index 20c0050..2dcbb68 100644
--- a/src/Common.cpp
+++ b/src/Common.cpp
@@ -204,6 +204,66 @@
     return result;

 }

 

+bool ConvertCharsToUnicode(std::wstring *outStr, const std::string &s, unsigned codePage)

+{

+    if (s.empty())

+    {

+        outStr->clear();

+        return true;

+    }

+

+    // Phase 1 - Get buffer size.

+    const int size = MultiByteToWideChar(codePage, 0, s.data(), (int)s.length(), NULL, 0);

+    if (size == 0)

+    {

+        outStr->clear();

+        return false;

+    }

+

+    // Phase 2 - Do conversion.

+    std::unique_ptr<wchar_t[]> buf(new wchar_t[(size_t)size]);

+    int result = MultiByteToWideChar(codePage, 0, s.data(), (int)s.length(), buf.get(), size);

+    if (result == 0)

+    {

+        outStr->clear();

+        return false;

+    }

+

+    outStr->assign(buf.get(), (size_t)size);

+    return true;

+}

+

+bool ConvertCharsToUnicode(std::wstring *outStr, const char *s, size_t sCharCount, unsigned codePage)

+{

+    if (sCharCount == 0)

+    {

+        outStr->clear();

+        return true;

+    }

+

+    assert(sCharCount <= (size_t)INT_MAX);

+

+    // Phase 1 - Get buffer size.

+    int size = MultiByteToWideChar(codePage, 0, s, (int)sCharCount, NULL, 0);

+    if (size == 0)

+    {

+        outStr->clear();

+        return false;

+    }

+

+    // Phase 2 - Do conversion.

+    std::unique_ptr<wchar_t[]> buf(new wchar_t[(size_t)size]);

+    int result = MultiByteToWideChar(codePage, 0, s, (int)sCharCount, buf.get(), size);

+    if (result == 0)

+    {

+        outStr->clear();

+        return false;

+    }

+

+    outStr->assign(buf.get(), (size_t)size);

+    return true;

+}

+

 const wchar_t* PhysicalDeviceTypeToStr(VkPhysicalDeviceType type)

 {

     // Skipping common prefix VK_PHYSICAL_DEVICE_TYPE_

diff --git a/src/Common.h b/src/Common.h
index 20db651..a718234 100644
--- a/src/Common.h
+++ b/src/Common.h
@@ -323,6 +323,9 @@
 void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize);

 

 std::wstring SizeToStr(size_t size);

+// As codePage use e.g. CP_ACP for native Windows 1-byte codepage or CP_UTF8.

+bool ConvertCharsToUnicode(std::wstring *outStr, const std::string &s, unsigned codePage);

+bool ConvertCharsToUnicode(std::wstring *outStr, const char *s, size_t sCharCount, unsigned codePage);

 

 const wchar_t* PhysicalDeviceTypeToStr(VkPhysicalDeviceType type);

 const wchar_t* VendorIDToStr(uint32_t vendorID);

diff --git a/src/VulkanSample.cpp b/src/VulkanSample.cpp
index e690f49..0d07cfb 100644
--- a/src/VulkanSample.cpp
+++ b/src/VulkanSample.cpp
@@ -27,6 +27,9 @@
 #include "VmaUsage.h"

 #include "Common.h"

 #include <atomic>

+#include <Shlwapi.h>

+

+#pragma comment(lib, "shlwapi.lib")

 

 static const char* const SHADER_PATH1 = "./";

 static const char* const SHADER_PATH2 = "../bin/";

@@ -40,6 +43,15 @@
 static void* const CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA = (void*)(intptr_t)43564544;

 static const bool USE_CUSTOM_CPU_ALLOCATION_CALLBACKS = true;

 

+enum class ExitCode : int

+{

+    GPUList = 2,

+    Help = 1,

+    Success = 0,

+    RuntimeError = -1,

+    CommandLineError = -2,

+};

+

 VkPhysicalDevice g_hPhysicalDevice;

 VkDevice g_hDevice;

 VmaAllocator g_hAllocator;

@@ -106,7 +118,6 @@
 static PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT_Func;

 static PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT_Func;

 static PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT_Func;

-static VkDebugUtilsMessengerEXT g_DebugUtilsMessenger;

 

 static VkQueue g_hGraphicsQueue;

 VkQueue g_hSparseBindingQueue;

@@ -179,6 +190,63 @@
 

 const VkAllocationCallbacks* g_Allocs;

 

+struct GPUSelection

+{

+    uint32_t Index = UINT32_MAX;

+    std::wstring Substring;

+};

+

+class VulkanUsage

+{

+public:

+    void Init();

+    ~VulkanUsage();

+    void PrintPhysicalDeviceList() const;

+    // If failed, returns VK_NULL_HANDLE.

+    VkPhysicalDevice SelectPhysicalDevice(const GPUSelection& GPUSelection) const;

+

+private:

+    VkDebugUtilsMessengerEXT m_DebugUtilsMessenger = VK_NULL_HANDLE;

+

+    void RegisterDebugCallbacks();

+    static bool IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, const char* pLayerName);

+};

+

+struct CommandLineParameters

+{

+    bool m_Help = false;

+    bool m_List = false;

+    GPUSelection m_GPUSelection;

+

+    bool Parse(int argc, wchar_t** argv)

+    {

+        for(int i = 1; i < argc; ++i)

+        {

+            if(_wcsicmp(argv[i], L"-h") == 0 || _wcsicmp(argv[i], L"--Help") == 0)

+            {

+                m_Help = true;

+            }

+            else if(_wcsicmp(argv[i], L"-l") == 0 || _wcsicmp(argv[i], L"--List") == 0)

+            {

+                m_List = true;

+            }

+            else if((_wcsicmp(argv[i], L"-g") == 0 || _wcsicmp(argv[i], L"--GPU") == 0) && i + 1 < argc)

+            {

+                m_GPUSelection.Substring = argv[i + 1];

+                ++i;

+            }

+            else if((_wcsicmp(argv[i], L"-i") == 0 || _wcsicmp(argv[i], L"--GPUIndex") == 0) && i + 1 < argc)

+            {

+                m_GPUSelection.Index = _wtoi(argv[i + 1]);

+                ++i;

+            }

+            else

+                return false;

+        }

+        return true;

+    }

+} g_CommandLineParameters;

+

 void SetDebugUtilsObjectName(VkObjectType type, uint64_t handle, const char* name)

 {

     if(vkSetDebugUtilsObjectNameEXT_Func == nullptr)

@@ -313,6 +381,223 @@
     return result;

 }

 

+static constexpr uint32_t GetVulkanApiVersion()

+{

+#if VMA_VULKAN_VERSION == 1002000

+    return VK_API_VERSION_1_2;

+#elif VMA_VULKAN_VERSION == 1001000

+    return VK_API_VERSION_1_1;

+#elif VMA_VULKAN_VERSION == 1000000

+    return VK_API_VERSION_1_0;

+#else

+#error Invalid VMA_VULKAN_VERSION.

+    return UINT32_MAX;

+#endif

+}

+

+void VulkanUsage::Init()

+{

+    g_hAppInstance = (HINSTANCE)GetModuleHandle(NULL);

+

+    if(USE_CUSTOM_CPU_ALLOCATION_CALLBACKS)

+    {

+        g_Allocs = &g_CpuAllocationCallbacks;

+    }

+

+    uint32_t instanceLayerPropCount = 0;

+    ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr) );

+    std::vector<VkLayerProperties> instanceLayerProps(instanceLayerPropCount);

+    if(instanceLayerPropCount > 0)

+    {

+        ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data()) );

+    }

+

+    if(g_EnableValidationLayer)

+    {

+        if(IsLayerSupported(instanceLayerProps.data(), instanceLayerProps.size(), VALIDATION_LAYER_NAME) == false)

+        {

+            wprintf(L"Layer \"%hs\" not supported.", VALIDATION_LAYER_NAME);

+            g_EnableValidationLayer = false;

+        }

+    }

+

+    uint32_t availableInstanceExtensionCount = 0;

+    ERR_GUARD_VULKAN( vkEnumerateInstanceExtensionProperties(nullptr, &availableInstanceExtensionCount, nullptr) );

+    std::vector<VkExtensionProperties> availableInstanceExtensions(availableInstanceExtensionCount);

+    if(availableInstanceExtensionCount > 0)

+    {

+        ERR_GUARD_VULKAN( vkEnumerateInstanceExtensionProperties(nullptr, &availableInstanceExtensionCount, availableInstanceExtensions.data()) );

+    }

+

+    std::vector<const char*> enabledInstanceExtensions;

+    enabledInstanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);

+    enabledInstanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);

+

+    std::vector<const char*> instanceLayers;

+    if(g_EnableValidationLayer)

+    {

+        instanceLayers.push_back(VALIDATION_LAYER_NAME);

+    }

+

+    for(const auto& extensionProperties : availableInstanceExtensions)

+    {

+        if(strcmp(extensionProperties.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0)

+        {

+            if(GetVulkanApiVersion() == VK_API_VERSION_1_0)

+            {   

+                enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);

+                VK_KHR_get_physical_device_properties2_enabled = true;

+            }

+        }

+        else if(strcmp(extensionProperties.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0)

+        {

+            enabledInstanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);

+            VK_EXT_debug_utils_enabled = true;

+        }

+    }

+

+    VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };

+    appInfo.pApplicationName = APP_TITLE_A;

+    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);

+    appInfo.pEngineName = "Adam Sawicki Engine";

+    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);

+    appInfo.apiVersion = GetVulkanApiVersion();

+

+    VkInstanceCreateInfo instInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };

+    instInfo.pApplicationInfo = &appInfo;

+    instInfo.enabledExtensionCount = static_cast<uint32_t>(enabledInstanceExtensions.size());

+    instInfo.ppEnabledExtensionNames = enabledInstanceExtensions.data();

+    instInfo.enabledLayerCount = static_cast<uint32_t>(instanceLayers.size());

+    instInfo.ppEnabledLayerNames = instanceLayers.data();

+

+    wprintf(L"Vulkan API version used: ");

+    switch(appInfo.apiVersion)

+    {

+    case VK_API_VERSION_1_0: wprintf(L"1.0\n"); break;

+    case VK_API_VERSION_1_1: wprintf(L"1.1\n"); break;

+    case VK_API_VERSION_1_2: wprintf(L"1.2\n"); break;

+    default: assert(0);

+    }

+

+    ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, g_Allocs, &g_hVulkanInstance) );

+

+    if(VK_EXT_debug_utils_enabled)

+    {

+        RegisterDebugCallbacks();

+    }

+}

+

+VulkanUsage::~VulkanUsage()

+{

+    if(m_DebugUtilsMessenger)

+    {

+        vkDestroyDebugUtilsMessengerEXT_Func(g_hVulkanInstance, m_DebugUtilsMessenger, g_Allocs);

+    }

+

+    if(g_hVulkanInstance)

+    {

+        vkDestroyInstance(g_hVulkanInstance, g_Allocs);

+        g_hVulkanInstance = VK_NULL_HANDLE;

+    }

+}

+

+void VulkanUsage::PrintPhysicalDeviceList() const

+{

+    uint32_t deviceCount = 0;

+    ERR_GUARD_VULKAN(vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, nullptr));

+    std::vector<VkPhysicalDevice> physicalDevices(deviceCount);

+    if(deviceCount > 0)

+    {

+        ERR_GUARD_VULKAN(vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, physicalDevices.data()));

+    }

+

+    for(size_t i = 0; i < deviceCount; ++i)

+    {

+        VkPhysicalDeviceProperties props = {};

+        vkGetPhysicalDeviceProperties(physicalDevices[i], &props);

+        wprintf(L"Physical device %zu: %hs\n", i, props.deviceName);

+    }

+}

+

+VkPhysicalDevice VulkanUsage::SelectPhysicalDevice(const GPUSelection& GPUSelection) const

+{

+    uint32_t deviceCount = 0;

+    ERR_GUARD_VULKAN(vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, nullptr));

+    std::vector<VkPhysicalDevice> physicalDevices(deviceCount);

+    if(deviceCount > 0)

+    {

+        ERR_GUARD_VULKAN(vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, physicalDevices.data()));

+    }

+

+    if(GPUSelection.Index != UINT32_MAX)

+    {

+        // Cannot specify both index and name.

+        if(!GPUSelection.Substring.empty())

+        {

+            return VK_NULL_HANDLE;

+        }

+

+        return GPUSelection.Index < deviceCount ? physicalDevices[GPUSelection.Index] : VK_NULL_HANDLE;

+    }

+

+    if(!GPUSelection.Substring.empty())

+    {

+        VkPhysicalDevice result = VK_NULL_HANDLE;

+        std::wstring name;

+        for(uint32_t i = 0; i < deviceCount; ++i)

+        {

+            VkPhysicalDeviceProperties props = {};

+            vkGetPhysicalDeviceProperties(physicalDevices[i], &props);

+            if(ConvertCharsToUnicode(&name, props.deviceName, strlen(props.deviceName), CP_UTF8) &&

+                StrStrI(name.c_str(), GPUSelection.Substring.c_str()))

+            {

+                // Second matching device found - error.

+                if(result != VK_NULL_HANDLE)

+                {

+                    return VK_NULL_HANDLE;

+                }

+                // First matching device found.

+                result = physicalDevices[i];

+            }

+        }

+        // Found or not, return it.

+        return result;

+    }

+

+    // Select first one.

+    return deviceCount > 0 ? physicalDevices[0] : VK_NULL_HANDLE;

+}

+

+void VulkanUsage::RegisterDebugCallbacks()

+{

+    vkCreateDebugUtilsMessengerEXT_Func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(

+        g_hVulkanInstance, "vkCreateDebugUtilsMessengerEXT");

+    vkDestroyDebugUtilsMessengerEXT_Func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(

+        g_hVulkanInstance, "vkDestroyDebugUtilsMessengerEXT");

+    vkSetDebugUtilsObjectNameEXT_Func = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(

+        g_hVulkanInstance, "vkSetDebugUtilsObjectNameEXT");

+    assert(vkCreateDebugUtilsMessengerEXT_Func);

+    assert(vkDestroyDebugUtilsMessengerEXT_Func);

+    assert(vkSetDebugUtilsObjectNameEXT_Func);

+

+    VkDebugUtilsMessengerCreateInfoEXT messengerCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };

+    messengerCreateInfo.messageSeverity = DEBUG_UTILS_MESSENGER_MESSAGE_SEVERITY;

+    messengerCreateInfo.messageType = DEBUG_UTILS_MESSENGER_MESSAGE_TYPE;

+    messengerCreateInfo.pfnUserCallback = MyDebugReportCallback;

+    ERR_GUARD_VULKAN( vkCreateDebugUtilsMessengerEXT_Func(g_hVulkanInstance, &messengerCreateInfo, g_Allocs, &m_DebugUtilsMessenger) );

+}

+

+bool VulkanUsage::IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, const char* pLayerName)

+{

+    const VkLayerProperties* propsEnd = pProps + propCount;

+    return std::find_if(

+        pProps,

+        propsEnd,

+        [pLayerName](const VkLayerProperties& prop) -> bool {

+        return strcmp(pLayerName, prop.layerName) == 0;

+    }) != propsEnd;

+}

+

 struct Vertex

 {

     float pos[3];

@@ -575,44 +860,6 @@
     mat4 ModelViewProj;

 };

 

-static void RegisterDebugCallbacks()

-{

-    vkCreateDebugUtilsMessengerEXT_Func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(

-        g_hVulkanInstance, "vkCreateDebugUtilsMessengerEXT");

-    vkDestroyDebugUtilsMessengerEXT_Func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(

-        g_hVulkanInstance, "vkDestroyDebugUtilsMessengerEXT");

-    vkSetDebugUtilsObjectNameEXT_Func = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(

-        g_hVulkanInstance, "vkSetDebugUtilsObjectNameEXT");

-    assert(vkCreateDebugUtilsMessengerEXT_Func);

-    assert(vkDestroyDebugUtilsMessengerEXT_Func);

-    assert(vkSetDebugUtilsObjectNameEXT_Func);

-

-    VkDebugUtilsMessengerCreateInfoEXT messengerCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };

-    messengerCreateInfo.messageSeverity = DEBUG_UTILS_MESSENGER_MESSAGE_SEVERITY;

-    messengerCreateInfo.messageType = DEBUG_UTILS_MESSENGER_MESSAGE_TYPE;

-    messengerCreateInfo.pfnUserCallback = MyDebugReportCallback;

-    ERR_GUARD_VULKAN( vkCreateDebugUtilsMessengerEXT_Func(g_hVulkanInstance, &messengerCreateInfo, g_Allocs, &g_DebugUtilsMessenger) );

-}

-

-static void UnregisterDebugCallbacks()

-{

-    if(g_DebugUtilsMessenger)

-    {

-        vkDestroyDebugUtilsMessengerEXT_Func(g_hVulkanInstance, g_DebugUtilsMessenger, g_Allocs);

-    }

-}

-

-static bool IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, const char* pLayerName)

-{

-    const VkLayerProperties* propsEnd = pProps + propCount;

-    return std::find_if(

-        pProps,

-        propsEnd,

-        [pLayerName](const VkLayerProperties& prop) -> bool {

-            return strcmp(pLayerName, prop.layerName) == 0;

-        }) != propsEnd;

-}

-

 static VkFormat FindSupportedFormat(

     const std::vector<VkFormat>& candidates,

     VkImageTiling tiling,

@@ -1110,20 +1357,6 @@
     }

 }

 

-static constexpr uint32_t GetVulkanApiVersion()

-{

-#if VMA_VULKAN_VERSION == 1002000

-    return VK_API_VERSION_1_2;

-#elif VMA_VULKAN_VERSION == 1001000

-    return VK_API_VERSION_1_1;

-#elif VMA_VULKAN_VERSION == 1000000

-    return VK_API_VERSION_1_0;

-#else

-    #error Invalid VMA_VULKAN_VERSION.

-    return UINT32_MAX;

-#endif

-}

-

 static void PrintEnabledFeatures()

 {

     wprintf(L"Enabled extensions and features:\n");

@@ -1522,93 +1755,6 @@
 

 static void InitializeApplication()

 {

-    if(USE_CUSTOM_CPU_ALLOCATION_CALLBACKS)

-    {

-        g_Allocs = &g_CpuAllocationCallbacks;

-    }

-

-    uint32_t instanceLayerPropCount = 0;

-    ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr) );

-    std::vector<VkLayerProperties> instanceLayerProps(instanceLayerPropCount);

-    if(instanceLayerPropCount > 0)

-    {

-        ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data()) );

-    }

-

-    if(g_EnableValidationLayer)

-    {

-        if(IsLayerSupported(instanceLayerProps.data(), instanceLayerProps.size(), VALIDATION_LAYER_NAME) == false)

-        {

-            wprintf(L"Layer \"%hs\" not supported.", VALIDATION_LAYER_NAME);

-            g_EnableValidationLayer = false;

-        }

-    }

-

-    uint32_t availableInstanceExtensionCount = 0;

-    ERR_GUARD_VULKAN( vkEnumerateInstanceExtensionProperties(nullptr, &availableInstanceExtensionCount, nullptr) );

-    std::vector<VkExtensionProperties> availableInstanceExtensions(availableInstanceExtensionCount);

-    if(availableInstanceExtensionCount > 0)

-    {

-        ERR_GUARD_VULKAN( vkEnumerateInstanceExtensionProperties(nullptr, &availableInstanceExtensionCount, availableInstanceExtensions.data()) );

-    }

-

-    std::vector<const char*> enabledInstanceExtensions;

-    enabledInstanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);

-    enabledInstanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);

-

-    std::vector<const char*> instanceLayers;

-    if(g_EnableValidationLayer)

-    {

-        instanceLayers.push_back(VALIDATION_LAYER_NAME);

-    }

-

-    for(const auto& extensionProperties : availableInstanceExtensions)

-    {

-        if(strcmp(extensionProperties.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0)

-        {

-            if(GetVulkanApiVersion() == VK_API_VERSION_1_0)

-            {   

-                enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);

-                VK_KHR_get_physical_device_properties2_enabled = true;

-            }

-        }

-        else if(strcmp(extensionProperties.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0)

-        {

-            enabledInstanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);

-            VK_EXT_debug_utils_enabled = true;

-        }

-    }

-

-    VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };

-    appInfo.pApplicationName = APP_TITLE_A;

-    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);

-    appInfo.pEngineName = "Adam Sawicki Engine";

-    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);

-    appInfo.apiVersion = GetVulkanApiVersion();

-

-    VkInstanceCreateInfo instInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };

-    instInfo.pApplicationInfo = &appInfo;

-    instInfo.enabledExtensionCount = static_cast<uint32_t>(enabledInstanceExtensions.size());

-    instInfo.ppEnabledExtensionNames = enabledInstanceExtensions.data();

-    instInfo.enabledLayerCount = static_cast<uint32_t>(instanceLayers.size());

-    instInfo.ppEnabledLayerNames = instanceLayers.data();

-

-    wprintf(L"Vulkan API version used: ");

-    switch(appInfo.apiVersion)

-    {

-    case VK_API_VERSION_1_0: wprintf(L"1.0\n"); break;

-    case VK_API_VERSION_1_1: wprintf(L"1.1\n"); break;

-    case VK_API_VERSION_1_2: wprintf(L"1.2\n"); break;

-    default: assert(0);

-    }

-

-    ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, g_Allocs, &g_hVulkanInstance) );

-

-    if(VK_EXT_debug_utils_enabled)

-    {

-        RegisterDebugCallbacks();

-    }

-

     // Create VkSurfaceKHR.

     VkWin32SurfaceCreateInfoKHR surfaceInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };

     surfaceInfo.hinstance = g_hAppInstance;

@@ -1616,17 +1762,6 @@
     VkResult result = vkCreateWin32SurfaceKHR(g_hVulkanInstance, &surfaceInfo, g_Allocs, &g_hSurface);

     assert(result == VK_SUCCESS);

 

-    // Find physical device

-

-    uint32_t deviceCount = 0;

-    ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, nullptr) );

-    assert(deviceCount > 0);

-

-    std::vector<VkPhysicalDevice> physicalDevices(deviceCount);

-    ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, physicalDevices.data()) );

-

-    g_hPhysicalDevice = physicalDevices[0];

-

     // Query for device extensions

 

     uint32_t physicalDeviceExtensionPropertyCount = 0;

@@ -2102,14 +2237,6 @@
         vkDestroySurfaceKHR(g_hVulkanInstance, g_hSurface, g_Allocs);

         g_hSurface = VK_NULL_HANDLE;

     }

-

-    UnregisterDebugCallbacks();

-

-    if(g_hVulkanInstance != VK_NULL_HANDLE)

-    {

-        vkDestroyInstance(g_hVulkanInstance, g_Allocs);

-        g_hVulkanInstance = VK_NULL_HANDLE;

-    }

 }

 

 static void PrintAllocatorStats()

@@ -2272,6 +2399,18 @@
     }

 }

 

+#define CATCH_PRINT_ERROR(extraCatchCode) \

+    catch(const std::exception& ex) \

+    { \

+        fwprintf(stderr, L"ERROR: %hs\n", ex.what()); \

+        extraCatchCode \

+    } \

+    catch(...) \

+    { \

+        fwprintf(stderr, L"UNKNOWN ERROR.\n"); \

+        extraCatchCode \

+    }

+

 static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)

 {

     switch(msg)

@@ -2279,12 +2418,20 @@
     case WM_CREATE:

         // This is intentionally assigned here because we are now inside CreateWindow, before it returns.

         g_hWnd = hWnd;

-        InitializeApplication();

+        try

+        {

+            InitializeApplication();

+        }

+        CATCH_PRINT_ERROR(return -1;)

         //PrintAllocatorStats();

         return 0;

 

     case WM_DESTROY:

-        FinalizeApplication();

+        try

+        {

+            FinalizeApplication();

+        }

+        CATCH_PRINT_ERROR(;)

         PostQuitMessage(0);

         return 0;

 

@@ -2296,11 +2443,21 @@
 

     case WM_SIZE:

         if((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))

-            HandlePossibleSizeChange();

+        {

+            try

+            {

+                HandlePossibleSizeChange();

+            }

+            CATCH_PRINT_ERROR(DestroyWindow(hWnd);)

+        }

         return 0;

 

     case WM_EXITSIZEMOVE:

-        HandlePossibleSizeChange();

+        try

+        {

+            HandlePossibleSizeChange();

+        }

+        CATCH_PRINT_ERROR(DestroyWindow(hWnd);)

         return 0;

 

     case WM_KEYDOWN:

@@ -2314,17 +2471,18 @@
             {

                 Test();

             }

-            catch(const std::exception& ex)

-            {

-                printf("ERROR: %s\n", ex.what());

-            }

+            CATCH_PRINT_ERROR(;)

             break;

         case 'S':

             try

             {

                 if(g_SparseBindingEnabled)

                 {

-                    TestSparseBinding();

+                    try

+                    {

+                        TestSparseBinding();

+                    }

+                    CATCH_PRINT_ERROR(;)

                 }

                 else

                 {

@@ -2346,10 +2504,24 @@
     return DefWindowProc(hWnd, msg, wParam, lParam);

 }

 

-int main()

+static void PrintLogo()

 {

-    g_hAppInstance = (HINSTANCE)GetModuleHandle(NULL);

+    wprintf(L"%s\n", APP_TITLE_W);

+}

 

+static void PrintHelp()

+{

+    wprintf(

+        L"Command line syntax:\n"

+        L"-h, --Help   Print this information\n"

+        L"-l, --List   Print list of GPUs\n"

+        L"-g S, --GPU S   Select GPU with name containing S\n"

+        L"-i N, --GPUIndex N   Select GPU index N\n"

+    );

+}

+

+int MainWindow()

+{

     WNDCLASSEX wndClassDesc = { sizeof(WNDCLASSEX) };

     wndClassDesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;

     wndClassDesc.hbrBackground = NULL;

@@ -2387,11 +2559,51 @@
             DrawFrame();

     }

 

-    TEST(g_CpuAllocCount.load() == 0);

-

-    return 0;

+    return (int)msg.wParam;;

 }

 

+int Main2(int argc, wchar_t** argv)

+{

+    PrintLogo();

+

+    if(!g_CommandLineParameters.Parse(argc, argv))

+    {

+        wprintf(L"ERROR: Invalid command line syntax.\n");

+        PrintHelp();

+        return (int)ExitCode::CommandLineError;

+    }

+

+    if(g_CommandLineParameters.m_Help)

+    {

+        PrintHelp();

+        return (int)ExitCode::Help;

+    }

+

+    VulkanUsage vulkanUsage;

+    vulkanUsage.Init();

+

+    if(g_CommandLineParameters.m_List)

+    {

+        vulkanUsage.PrintPhysicalDeviceList();

+        return (int)ExitCode::GPUList;

+    }

+

+    g_hPhysicalDevice = vulkanUsage.SelectPhysicalDevice(g_CommandLineParameters.m_GPUSelection);

+    TEST(g_hPhysicalDevice);

+

+    return MainWindow();

+}

+

+int wmain(int argc, wchar_t** argv)

+{

+    try

+    {

+        return Main2(argc, argv);

+        TEST(g_CpuAllocCount.load() == 0);

+    }

+    CATCH_PRINT_ERROR(return (int)ExitCode::RuntimeError;)

+} 

+

 #else // #ifdef _WIN32

 

 #include "VmaUsage.h"