create vulkan device

This commit is contained in:
Ivan Avdeev 2021-01-09 17:14:32 -08:00
parent 0dcedece06
commit 0170f3e408
1 changed files with 264 additions and 114 deletions

View File

@ -12,25 +12,11 @@
#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>
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,41 +153,58 @@ 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)
{
gEngine.Con_Printf( S_ERROR "Cannot initialize Vulkan video" );
return false;
for (int i = 0; i < count; ++i)
{
*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);
}
}
}
vk_core.get_proc_addr = gEngine.VK_GetVkGetInstanceProcAddr();
if (!vk_core.get_proc_addr)
static void loadDeviceFunctions(dllfunc_t *funcs, int count)
{
gEngine.Con_Printf( S_ERROR "Cannot get vkGetInstanceProcAddr address" );
return false;
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);
}
}
}
vk_core.pool = Mem_AllocPool("Vulkan pool");
loadInstanceFunctions(nullinst_funcs, ARRAYSIZE(nullinst_funcs));
if (vkEnumerateInstanceVersion)
{
vkEnumerateInstanceVersion(&vk_core.vulkan_version);
}
else
{
vk_core.vulkan_version = VK_MAKE_VERSION(1, 0, 0);
}
gEngine.Con_Printf( "Vulkan version %u.%u.%u\n", XVK_PARSE_VERSION(vk_core.vulkan_version));
static qboolean createInstance( void )
{
const char **instance_extensions = NULL;
unsigned int num_instance_extensions = debug ? 1 : 0;
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,
@ -231,7 +236,7 @@ qboolean R_VkInit( void )
return false;
}
if (debug)
if (vk_core.debug)
{
instance_extensions[vid_extensions] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
}
@ -245,7 +250,7 @@ qboolean R_VkInit( void )
create_info.enabledExtensionCount = num_instance_extensions;
create_info.ppEnabledExtensionNames = instance_extensions;
if (debug)
if (vk_core.debug)
{
create_info.enabledLayerCount = ARRAYSIZE(validation_layers);
create_info.ppEnabledLayerNames = validation_layers;
@ -258,7 +263,7 @@ qboolean R_VkInit( void )
loadInstanceFunctions(instance_funcs, ARRAYSIZE(instance_funcs));
if (debug)
if (vk_core.debug)
{
loadInstanceFunctions(instance_debug_funcs, ARRAYSIZE(instance_debug_funcs));
@ -278,8 +283,151 @@ qboolean R_VkInit( void )
}
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;
}
vk_core.pool = Mem_AllocPool("Vulkan pool");
loadInstanceFunctions(nullinst_funcs, ARRAYSIZE(nullinst_funcs));
if (vkEnumerateInstanceVersion)
{
vkEnumerateInstanceVersion(&vk_core.vulkan_version);
}
else
{
vk_core.vulkan_version = VK_MAKE_VERSION(1, 0, 0);
}
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)
{
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);
}