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.
This commit is contained in:
Adam Sawicki 2021-03-11 15:15:38 +01:00
parent 48e3d88114
commit a75a61bfd7
3 changed files with 448 additions and 173 deletions

View File

@ -204,6 +204,66 @@ std::wstring SizeToStr(size_t size)
return result; 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) const wchar_t* PhysicalDeviceTypeToStr(VkPhysicalDeviceType type)
{ {
// Skipping common prefix VK_PHYSICAL_DEVICE_TYPE_ // Skipping common prefix VK_PHYSICAL_DEVICE_TYPE_

View File

@ -323,6 +323,9 @@ void PrintErrorF(const wchar_t* format, ...);
void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize); void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize);
std::wstring SizeToStr(size_t size); 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* PhysicalDeviceTypeToStr(VkPhysicalDeviceType type);
const wchar_t* VendorIDToStr(uint32_t vendorID); const wchar_t* VendorIDToStr(uint32_t vendorID);

View File

@ -27,6 +27,9 @@
#include "VmaUsage.h" #include "VmaUsage.h"
#include "Common.h" #include "Common.h"
#include <atomic> #include <atomic>
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
static const char* const SHADER_PATH1 = "./"; static const char* const SHADER_PATH1 = "./";
static const char* const SHADER_PATH2 = "../bin/"; static const char* const SHADER_PATH2 = "../bin/";
@ -40,6 +43,15 @@ static const uint32_t COMMAND_BUFFER_COUNT = 2;
static void* const CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA = (void*)(intptr_t)43564544; static void* const CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA = (void*)(intptr_t)43564544;
static const bool USE_CUSTOM_CPU_ALLOCATION_CALLBACKS = true; 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; VkPhysicalDevice g_hPhysicalDevice;
VkDevice g_hDevice; VkDevice g_hDevice;
VmaAllocator g_hAllocator; VmaAllocator g_hAllocator;
@ -106,7 +118,6 @@ static const VkDebugUtilsMessageTypeFlagsEXT DEBUG_UTILS_MESSENGER_MESSAGE_TYPE
static PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT_Func; static PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT_Func;
static PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT_Func; static PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT_Func;
static PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT_Func; static PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT_Func;
static VkDebugUtilsMessengerEXT g_DebugUtilsMessenger;
static VkQueue g_hGraphicsQueue; static VkQueue g_hGraphicsQueue;
VkQueue g_hSparseBindingQueue; VkQueue g_hSparseBindingQueue;
@ -179,6 +190,63 @@ static const VkAllocationCallbacks g_CpuAllocationCallbacks = {
const VkAllocationCallbacks* g_Allocs; 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) void SetDebugUtilsObjectName(VkObjectType type, uint64_t handle, const char* name)
{ {
if(vkSetDebugUtilsObjectNameEXT_Func == nullptr) if(vkSetDebugUtilsObjectNameEXT_Func == nullptr)
@ -313,6 +381,223 @@ static VkExtent2D ChooseSwapExtent()
return result; 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 struct Vertex
{ {
float pos[3]; float pos[3];
@ -575,44 +860,6 @@ struct UniformBufferObject
mat4 ModelViewProj; 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( static VkFormat FindSupportedFormat(
const std::vector<VkFormat>& candidates, const std::vector<VkFormat>& candidates,
VkImageTiling tiling, VkImageTiling tiling,
@ -1110,20 +1357,6 @@ static void DestroySwapchain(bool destroyActualSwapchain)
} }
} }
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() static void PrintEnabledFeatures()
{ {
wprintf(L"Enabled extensions and features:\n"); wprintf(L"Enabled extensions and features:\n");
@ -1522,93 +1755,6 @@ static void PrintMemoryConclusions()
static void InitializeApplication() 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. // Create VkSurfaceKHR.
VkWin32SurfaceCreateInfoKHR surfaceInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR }; VkWin32SurfaceCreateInfoKHR surfaceInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };
surfaceInfo.hinstance = g_hAppInstance; surfaceInfo.hinstance = g_hAppInstance;
@ -1616,17 +1762,6 @@ static void InitializeApplication()
VkResult result = vkCreateWin32SurfaceKHR(g_hVulkanInstance, &surfaceInfo, g_Allocs, &g_hSurface); VkResult result = vkCreateWin32SurfaceKHR(g_hVulkanInstance, &surfaceInfo, g_Allocs, &g_hSurface);
assert(result == VK_SUCCESS); 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 // Query for device extensions
uint32_t physicalDeviceExtensionPropertyCount = 0; uint32_t physicalDeviceExtensionPropertyCount = 0;
@ -2102,14 +2237,6 @@ static void FinalizeApplication()
vkDestroySurfaceKHR(g_hVulkanInstance, g_hSurface, g_Allocs); vkDestroySurfaceKHR(g_hVulkanInstance, g_hSurface, g_Allocs);
g_hSurface = VK_NULL_HANDLE; 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() static void PrintAllocatorStats()
@ -2272,6 +2399,18 @@ static void HandlePossibleSizeChange()
} }
} }
#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) static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
switch(msg) switch(msg)
@ -2279,12 +2418,20 @@ static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_CREATE: case WM_CREATE:
// This is intentionally assigned here because we are now inside CreateWindow, before it returns. // This is intentionally assigned here because we are now inside CreateWindow, before it returns.
g_hWnd = hWnd; g_hWnd = hWnd;
try
{
InitializeApplication(); InitializeApplication();
}
CATCH_PRINT_ERROR(return -1;)
//PrintAllocatorStats(); //PrintAllocatorStats();
return 0; return 0;
case WM_DESTROY: case WM_DESTROY:
try
{
FinalizeApplication(); FinalizeApplication();
}
CATCH_PRINT_ERROR(;)
PostQuitMessage(0); PostQuitMessage(0);
return 0; return 0;
@ -2296,11 +2443,21 @@ static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_SIZE: case WM_SIZE:
if((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)) if((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))
{
try
{
HandlePossibleSizeChange(); HandlePossibleSizeChange();
}
CATCH_PRINT_ERROR(DestroyWindow(hWnd);)
}
return 0; return 0;
case WM_EXITSIZEMOVE: case WM_EXITSIZEMOVE:
try
{
HandlePossibleSizeChange(); HandlePossibleSizeChange();
}
CATCH_PRINT_ERROR(DestroyWindow(hWnd);)
return 0; return 0;
case WM_KEYDOWN: case WM_KEYDOWN:
@ -2314,18 +2471,19 @@ static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
Test(); Test();
} }
catch(const std::exception& ex) CATCH_PRINT_ERROR(;)
{
printf("ERROR: %s\n", ex.what());
}
break; break;
case 'S': case 'S':
try try
{ {
if(g_SparseBindingEnabled) if(g_SparseBindingEnabled)
{
try
{ {
TestSparseBinding(); TestSparseBinding();
} }
CATCH_PRINT_ERROR(;)
}
else else
{ {
printf("Sparse binding not supported.\n"); printf("Sparse binding not supported.\n");
@ -2346,10 +2504,24 @@ static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
return DefWindowProc(hWnd, msg, wParam, lParam); 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) }; WNDCLASSEX wndClassDesc = { sizeof(WNDCLASSEX) };
wndClassDesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS; wndClassDesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
wndClassDesc.hbrBackground = NULL; wndClassDesc.hbrBackground = NULL;
@ -2387,9 +2559,49 @@ int main()
DrawFrame(); DrawFrame();
} }
TEST(g_CpuAllocCount.load() == 0); return (int)msg.wParam;;
}
return 0; 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 #else // #ifdef _WIN32