From 0170f3e408a5cfd2aa9ad7e1cda5ebef85dcc076 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Sat, 9 Jan 2021 17:14:32 -0800 Subject: [PATCH] create vulkan device --- ref_vk/vk_core.c | 378 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 264 insertions(+), 114 deletions(-) diff --git a/ref_vk/vk_core.c b/ref_vk/vk_core.c index 0fdc427e..403abd8e 100644 --- a/ref_vk/vk_core.c +++ b/ref_vk/vk_core.c @@ -12,25 +12,11 @@ #define VK_NO_PROTOTYPES #include -typedef struct vulkan_core_s { - PFN_vkGetInstanceProcAddr get_proc_addr; - uint32_t vulkan_version; - VkInstance instance; - VkDebugUtilsMessengerEXT debug_messenger; - - byte *pool; -} vulkan_core_t; - -vulkan_core_t vk_core = {0}; - #define XVK_PARSE_VERSION(v) \ VK_VERSION_MAJOR(v), \ VK_VERSION_MINOR(v), \ VK_VERSION_PATCH(v) -#define XVK_INSTANCE_FUNC(f) \ - ((PFN_ ##f)vk_core.get_proc_addr(vk_core.instance, #f)) - #define NULLINST_FUNCS(X) \ X(vkEnumerateInstanceVersion) \ X(vkCreateInstance) \ @@ -44,15 +30,23 @@ vulkan_core_t vk_core = {0}; X(vkGetPhysicalDeviceSurfaceSupportKHR) \ X(vkGetPhysicalDeviceMemoryProperties) \ X(vkCreateDevice) \ + X(vkGetDeviceProcAddr) \ + X(vkGetPhysicalDeviceProperties) \ #define INSTANCE_DEBUG_FUNCS(X) \ X(vkCreateDebugUtilsMessengerEXT) \ X(vkDestroyDebugUtilsMessengerEXT) \ +#define DEVICE_FUNCS(X) \ + X(vkGetDeviceQueue) \ + +static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + #define X(f) PFN_##f f = NULL; NULLINST_FUNCS(X) INSTANCE_FUNCS(X) INSTANCE_DEBUG_FUNCS(X) + DEVICE_FUNCS(X) #undef X static dllfunc_t nullinst_funcs[] = { @@ -73,6 +67,12 @@ static dllfunc_t instance_debug_funcs[] = { #undef X }; +static dllfunc_t device_funcs[] = { +#define X(f) {#f, (void**)&f}, + DEVICE_FUNCS(X) +#undef X +}; + static const char *resultName(VkResult result) { switch (result) { case VK_SUCCESS: return "VK_SUCCESS"; @@ -130,18 +130,6 @@ static const char *validation_layers[] = { "VK_LAYER_KHRONOS_validation", }; -static void loadInstanceFunctions(dllfunc_t *funcs, int count) -{ - for (int i = 0; i < count; ++i) - { - *funcs[i].func = vk_core.get_proc_addr(vk_core.instance, funcs[i].name); - if (!*funcs[i].func) - { - gEngine.Con_Printf( S_WARN "Function %s was not loaded\n", funcs[i].name); - } - } -} - VkBool32 debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, @@ -165,20 +153,249 @@ VkBool32 debugCallback( return VK_FALSE; } -qboolean R_VkInit( void ) -{ - const qboolean debug = !!(gEngine.Sys_CheckParm("-vkdebug") || gEngine.Sys_CheckParm("-gldebug")); +typedef struct physical_device_s { + VkPhysicalDevice device; + VkPhysicalDeviceMemoryProperties memory_properties; + VkPhysicalDeviceProperties properties; +} physical_device_t; - if( !gEngine.R_Init_Video( REF_VULKAN )) // request Vulkan surface +typedef struct vulkan_core_s { + uint32_t vulkan_version; + VkInstance instance; + VkDebugUtilsMessengerEXT debug_messenger; + + byte *pool; + + qboolean debug; + + VkSurfaceKHR surface; + + physical_device_t physical_device; + VkDevice device; + VkQueue queue; +} vulkan_core_t; + +vulkan_core_t vk_core = {0}; + +static void loadInstanceFunctions(dllfunc_t *funcs, int count) +{ + for (int i = 0; i < count; ++i) { - gEngine.Con_Printf( S_ERROR "Cannot initialize Vulkan video" ); + *funcs[i].func = vkGetInstanceProcAddr(vk_core.instance, funcs[i].name); + if (!*funcs[i].func) + { + gEngine.Con_Printf( S_WARN "Function %s was not loaded\n", funcs[i].name); + } + } +} + +static void loadDeviceFunctions(dllfunc_t *funcs, int count) +{ + for (int i = 0; i < count; ++i) + { + *funcs[i].func = vkGetDeviceProcAddr(vk_core.device, funcs[i].name); + if (!*funcs[i].func) + { + gEngine.Con_Printf( S_WARN "Function %s was not loaded\n", funcs[i].name); + } + } +} + +static qboolean createInstance( void ) +{ + const char **instance_extensions = NULL; + unsigned int num_instance_extensions = vk_core.debug ? 1 : 0; + VkApplicationInfo app_info = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .apiVersion = VK_API_VERSION_1_0, + .applicationVersion = VK_MAKE_VERSION(0, 0, 0), // TODO + .engineVersion = VK_MAKE_VERSION(0, 0, 0), + .pApplicationName = "", + .pEngineName = "xash3d-fwgs", + }; + VkInstanceCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &app_info, + }; + + int vid_extensions = gEngine.VK_GetInstanceExtensions(0, NULL); + if (vid_extensions < 0) + { + gEngine.Con_Printf( S_ERROR "Cannot get Vulkan instance extensions\n" ); return false; } - vk_core.get_proc_addr = gEngine.VK_GetVkGetInstanceProcAddr(); - if (!vk_core.get_proc_addr) + num_instance_extensions += vid_extensions; + + instance_extensions = Mem_Malloc(vk_core.pool, sizeof(const char*) * num_instance_extensions); + vid_extensions = gEngine.VK_GetInstanceExtensions(vid_extensions, instance_extensions); + if (vid_extensions < 0) { - gEngine.Con_Printf( S_ERROR "Cannot get vkGetInstanceProcAddr address" ); + gEngine.Con_Printf( S_ERROR "Cannot get Vulkan instance extensions\n" ); + Mem_Free(instance_extensions); + return false; + } + + if (vk_core.debug) + { + instance_extensions[vid_extensions] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + } + + gEngine.Con_Reportf("Requesting instance extensions: %d\n", num_instance_extensions); + for (int i = 0; i < num_instance_extensions; ++i) + { + gEngine.Con_Reportf("\t%d: %s\n", i, instance_extensions[i]); + } + + create_info.enabledExtensionCount = num_instance_extensions; + create_info.ppEnabledExtensionNames = instance_extensions; + + if (vk_core.debug) + { + create_info.enabledLayerCount = ARRAYSIZE(validation_layers); + create_info.ppEnabledLayerNames = validation_layers; + + gEngine.Con_Printf(S_WARN "Using Vulkan validation layers, expect severely degraded performance\n"); + } + + // TODO handle errors gracefully -- let it try next renderer + XVK_CHECK(vkCreateInstance(&create_info, NULL, &vk_core.instance)); + + loadInstanceFunctions(instance_funcs, ARRAYSIZE(instance_funcs)); + + if (vk_core.debug) + { + loadInstanceFunctions(instance_debug_funcs, ARRAYSIZE(instance_debug_funcs)); + + if (vkCreateDebugUtilsMessengerEXT) + { + VkDebugUtilsMessengerCreateInfoEXT debug_create_info = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .messageSeverity = 0x1111, //:vovka: VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, + .messageType = 0x07, + .pfnUserCallback = debugCallback, + }; + XVK_CHECK(vkCreateDebugUtilsMessengerEXT(vk_core.instance, &debug_create_info, NULL, &vk_core.debug_messenger)); + } else + { + gEngine.Con_Printf(S_WARN "Vulkan debug utils messenger is not available\n"); + } + } + + Mem_Free(instance_extensions); + return true; +} + +qboolean createDevice( void ) +{ + VkPhysicalDevice *physical_devices = NULL; + uint32_t num_physical_devices = 0; + uint32_t best_device_index = UINT32_MAX; + uint32_t queue_index = UINT32_MAX; + + XVK_CHECK(vkEnumeratePhysicalDevices(vk_core.instance, &num_physical_devices, physical_devices)); + + physical_devices = Mem_Malloc(vk_core.pool, sizeof(VkPhysicalDevice) * num_physical_devices); + XVK_CHECK(vkEnumeratePhysicalDevices(vk_core.instance, &num_physical_devices, physical_devices)); + + gEngine.Con_Reportf("Have %u devices:\n", num_physical_devices); + for (uint32_t i = 0; i < num_physical_devices; ++i) + { + VkQueueFamilyProperties *queue_family_props = NULL; + uint32_t num_queue_family_properties = 0; + VkPhysicalDeviceProperties props; + + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &num_queue_family_properties, queue_family_props); + queue_family_props = Mem_Malloc(vk_core.pool, sizeof(VkQueueFamilyProperties) * num_queue_family_properties); + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &num_queue_family_properties, queue_family_props); + + vkGetPhysicalDeviceProperties(physical_devices[i], &props); + gEngine.Con_Reportf("\t%u: %04x:%04x %d %s %u.%u.%u %u.%u.%u\n", + i, props.vendorID, props.deviceID, props.deviceType, props.deviceName, + XVK_PARSE_VERSION(props.driverVersion), XVK_PARSE_VERSION(props.apiVersion)); + + for (uint32_t j = 0; j < num_queue_family_properties; ++j) + { + VkBool32 present = 0; + if (!(queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) + continue; + + vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, vk_core.surface, &present); + + if (!present) + continue; + + queue_index = i; + break; + } + + Mem_Free(queue_family_props); + + // TODO pick the best device + // For now we'll pick the first one that has graphics and can present to the surface + if (queue_index < num_queue_family_properties) + { + best_device_index = i; + break; + } + } + + if (best_device_index < num_physical_devices) + { + float prio = 1.f; + VkDeviceQueueCreateInfo queue_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .flags = 0, + .queueFamilyIndex = queue_index, + .queueCount = 1, + .pQueuePriorities = &prio, + }; + const char *device_extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + VkDeviceCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .flags = 0, + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queue_info, + .enabledExtensionCount = ARRAYSIZE(device_extensions), + .ppEnabledExtensionNames = device_extensions, + }; + + vk_core.physical_device.device = physical_devices[best_device_index]; + vkGetPhysicalDeviceMemoryProperties(vk_core.physical_device.device, &vk_core.physical_device.memory_properties); + + vkGetPhysicalDeviceProperties(vk_core.physical_device.device, &vk_core.physical_device.properties); + gEngine.Con_Printf("Picked device #%u: %04x:%04x %d %s %u.%u.%u %u.%u.%u\n", + best_device_index, vk_core.physical_device.properties.vendorID, vk_core.physical_device.properties.deviceID, vk_core.physical_device.properties.deviceType, vk_core.physical_device.properties.deviceName, + XVK_PARSE_VERSION(vk_core.physical_device.properties.driverVersion), XVK_PARSE_VERSION(vk_core.physical_device.properties.apiVersion)); + + // TODO allow it to fail gracefully + XVK_CHECK(vkCreateDevice(vk_core.physical_device.device, &create_info, NULL, &vk_core.device)); + + loadDeviceFunctions(device_funcs, ARRAYSIZE(device_funcs)); + + vkGetDeviceQueue(vk_core.device, 0, 0, &vk_core.queue); + } + + Mem_Free(physical_devices); + return true; +} + +qboolean R_VkInit( void ) +{ + vk_core.debug = !!(gEngine.Sys_CheckParm("-vkdebug") || gEngine.Sys_CheckParm("-gldebug")); + + if( !gEngine.R_Init_Video( REF_VULKAN )) // request Vulkan surface + { + gEngine.Con_Printf( S_ERROR "Cannot initialize Vulkan video\n" ); + return false; + } + + vkGetInstanceProcAddr = gEngine.VK_GetVkGetInstanceProcAddr(); + if (!vkGetInstanceProcAddr) + { + gEngine.Con_Printf( S_ERROR "Cannot get vkGetInstanceProcAddr address\n" ); return false; } @@ -197,89 +414,20 @@ qboolean R_VkInit( void ) gEngine.Con_Printf( "Vulkan version %u.%u.%u\n", XVK_PARSE_VERSION(vk_core.vulkan_version)); + if (!createInstance()) + return false; + + vk_core.surface = gEngine.VK_CreateSurface(vk_core.instance); + if (!vk_core.surface) { - const char **instance_extensions = NULL; - unsigned int num_instance_extensions = debug ? 1 : 0; - VkApplicationInfo app_info = { - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .apiVersion = VK_API_VERSION_1_0, - .applicationVersion = VK_MAKE_VERSION(0, 0, 0), // TODO - .engineVersion = VK_MAKE_VERSION(0, 0, 0), - .pApplicationName = "", - .pEngineName = "xash3d-fwgs", - }; - VkInstanceCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pApplicationInfo = &app_info, - }; - - int vid_extensions = gEngine.VK_GetInstanceExtensions(0, NULL); - if (vid_extensions < 0) - { - gEngine.Con_Printf( S_ERROR "Cannot get Vulkan instance extensions\n" ); - return false; - } - - num_instance_extensions += vid_extensions; - - instance_extensions = Mem_Malloc(vk_core.pool, sizeof(const char*) * num_instance_extensions); - vid_extensions = gEngine.VK_GetInstanceExtensions(vid_extensions, instance_extensions); - if (vid_extensions < 0) - { - gEngine.Con_Printf( S_ERROR "Cannot get Vulkan instance extensions\n" ); - Mem_Free(instance_extensions); - return false; - } - - if (debug) - { - instance_extensions[vid_extensions] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; - } - - gEngine.Con_Reportf("Requesting instance extensions: %d\n", num_instance_extensions); - for (int i = 0; i < num_instance_extensions; ++i) - { - gEngine.Con_Reportf("\t%d: %s\n", i, instance_extensions[i]); - } - - create_info.enabledExtensionCount = num_instance_extensions; - create_info.ppEnabledExtensionNames = instance_extensions; - - if (debug) - { - create_info.enabledLayerCount = ARRAYSIZE(validation_layers); - create_info.ppEnabledLayerNames = validation_layers; - - gEngine.Con_Printf(S_WARN "Using Vulkan validation layers, expect severely degraded performance\n"); - } - - // TODO handle errors gracefully -- let it try next renderer - XVK_CHECK(vkCreateInstance(&create_info, NULL, &vk_core.instance)); - - loadInstanceFunctions(instance_funcs, ARRAYSIZE(instance_funcs)); - - if (debug) - { - loadInstanceFunctions(instance_debug_funcs, ARRAYSIZE(instance_debug_funcs)); - - if (vkCreateDebugUtilsMessengerEXT) - { - VkDebugUtilsMessengerCreateInfoEXT debug_create_info = { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, - .messageSeverity = 0x1111, //:vovka: VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, - .messageType = 0x07, - .pfnUserCallback = debugCallback, - }; - XVK_CHECK(vkCreateDebugUtilsMessengerEXT(vk_core.instance, &debug_create_info, NULL, &vk_core.debug_messenger)); - } else - { - gEngine.Con_Printf(S_WARN "Vulkan debug utils messenger is not available\n"); - } - } - - Mem_Free(instance_extensions); + gEngine.Con_Printf( S_ERROR "Cannot create Vulkan surface\n" ); + // FIXME destroy surface + return false; } + if (!createDevice()) + return false; + initTextures(); return true; @@ -293,5 +441,7 @@ void R_VkShutdown( void ) } vkDestroyInstance(vk_core.instance, NULL); + + Mem_FreePool(&vk_core.pool); }