diff options
author | kartofen <mladenovnasko0@gmail.com> | 2023-10-16 23:46:15 +0300 |
---|---|---|
committer | kartofen <mladenovnasko0@gmail.com> | 2023-10-16 23:46:15 +0300 |
commit | 5e910c7e41d18dab95e34ceb6e620a8958197ae6 (patch) | |
tree | bf49c8ca6dd123d0765beef63d5858445dcf24aa /src/device.c |
things done
Diffstat (limited to 'src/device.c')
-rw-r--r-- | src/device.c | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..fdcc7ad --- /dev/null +++ b/src/device.c @@ -0,0 +1,395 @@ +#include <string.h> +#include <vulkan/vulkan.h> + +#include "device.h" +#include "common.h" + +// TODO: rename device.c/.h +// TODO: add error checking +// TODO: add log output + +#ifdef DEBUG +char *validation_layers[] = { + "VK_LAYER_KHRONOS_validation" +}; +#endif + +enum { + QUEUE_FAMILY_COUNT = 2, + + QUEUE_FAMILY_GRAPHICS_FAMILY = 1 << 0, + QUEUE_FAMILY_PRESENT_FAMILY = 1 << 1, + + QUEUE_FAMILY_ALL = QUEUE_FAMILY_GRAPHICS_FAMILY | + QUEUE_FAMILY_PRESENT_FAMILY, +}; + +struct queue_family_idx { + uint32_t graphics_family; + uint32_t present_family; +}; + +// most function that return an int follow this scheme: +// 0 - success; 1 - error; (sometimes -1 - fatal error) + +// if the function has 'has' or 'is', then +// 0 - fail; 1 - success (sometimes -1 - fatal error) + +static int create_instance(device_t device, struct device_info *info); +static int create_surface(device_t device, struct device_info *info); +static int create_physical_device(device_t device, struct device_info *info); +static int create_logical_device(device_t device, struct device_info *info); + +static int is_device_suitable(VkPhysicalDevice phy_device, VkSurfaceKHR surface); + +// does not follow the general scheme, see implementation for details +static int find_queue_families(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct queue_family_idx *queue_family); + +#ifdef DEBUG +static int create_debug_messenger(device_t device, struct device_info* info); +static int has_validation_layer_support(const char * const *layers, uint32_t nlayers); +static void populate_debug_messenger_info(VkDebugUtilsMessengerCreateInfoEXT *info); + +VkResult CreateDebugUtilsMessengerEXT( + VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pDebugMessenger); +void DestroyDebugUtilsMessengerEXT( + VkInstance instance, + VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks* pAllocator); +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); +#endif + +#define CHECK(func) if(func(device, info)) { goto fail; } + +device_t device_create(struct device_info *info) +{ + device_t device = xmalloc(sizeof(struct device)); + + CHECK(create_instance); + CHECK(create_surface); +#ifdef DEBUG + CHECK(create_debug_messenger); +#endif + CHECK(create_physical_device); + CHECK(create_logical_device); + + return device; +fail: + device_destroy(device); + return NULL; +} + +void device_destroy(device_t device) +{ + if(!device) return; + + vkDestroyDevice(device->logical_device, NULL); + + #ifdef DEBUG + DestroyDebugUtilsMessengerEXT(device->instance, device->debug_messenger, NULL); + #endif + + vkDestroySurfaceKHR(device->instance, device->surface, NULL); + + vkDestroyInstance(device->instance, NULL); + + free(device); +} + +static int create_instance(device_t device, struct device_info *info) +{ + VkApplicationInfo app_info = {0}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pEngineName = "Engine"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_API_VERSION_1_0; + + app_info.pApplicationName = info->name; + app_info.applicationVersion = info->version; + + VkInstanceCreateInfo create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + create_info.enabledLayerCount = 0; + +#ifdef DEBUG + // add debug utils extensions for debug callback + char **extensions = xcalloc(info->ext_count+1, sizeof(char *)); + extensions[info->ext_count] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + + for(uint32_t i = 0; i < info->ext_count; i++) { + extensions[i] = (char *)info->extensions[i]; + } + + free((void *)info->extensions); + + // we just added one more extension + create_info.enabledExtensionCount = info->ext_count+1; + create_info.ppEnabledExtensionNames = (const char * const *)extensions; + + // validation layer support + char *validation_layers[] = { + "VK_LAYER_KHRONOS_validation" + }; + + uint32_t nlayers = ARR_SIZE(validation_layers); + + if(!has_validation_layer_support((const char * const *)validation_layers, nlayers)) { + err("validation_layer_support: failed"); + goto fail; + } + + create_info.ppEnabledLayerNames = (const char * const *)validation_layers; + create_info.enabledLayerCount = nlayers; + + // add the debug messenger for instance creation and destruction + VkDebugUtilsMessengerCreateInfoEXT msg_info = {0}; + populate_debug_messenger_info(&msg_info); + + create_info.pNext = &msg_info; +#endif + + if(vkCreateInstance(&create_info, NULL, &device->instance) != VK_SUCCESS) { + err("vkCreateInstance: failed"); + goto fail; + } + +#ifdef DEBUG + free(extensions); +#endif + + return 0; +fail: + return 1; +} + +static int create_surface(device_t device, struct device_info *info) +{ + if(info->surface_func(device->instance, &device->surface)) { + err("Couldn't create a VkSurfaceKHR"); + return 1; + } + + return 0; +} + +static int create_physical_device(device_t device, struct device_info *info) +{ + (void)info; + + device->physical_device = VK_NULL_HANDLE; + + uint32_t dev_count = 0; + vkEnumeratePhysicalDevices(device->instance, &dev_count, NULL); + + if(dev_count == 0) { + err("No physical devices could be found!"); + goto fail; + } + + VkPhysicalDevice *devices = xcalloc(dev_count, sizeof(VkPhysicalDevice)); + vkEnumeratePhysicalDevices(device->instance, &dev_count, devices); + + for(uint32_t i = 0; i < dev_count; i++) { + if(is_device_suitable(devices[i], device->surface)) { + device->physical_device = devices[i]; + break; + } + } + + free(devices); + + if(device->physical_device == VK_NULL_HANDLE) { + err("No suitable physical device could be found"); + goto fail; + } + + return 0; +fail: + return 1; +} + +static int create_logical_device(device_t device, struct device_info *info) +{ + (void)info; + + // queue family data + struct queue_family_idx indices = {0}; + find_queue_families(device->physical_device, device->surface, &indices); + + // queue infos + VkDeviceQueueCreateInfo *queue_infos = xcalloc(QUEUE_FAMILY_COUNT, sizeof(VkDeviceQueueCreateInfo)); + memset(queue_infos, 0, QUEUE_FAMILY_COUNT * sizeof(VkDeviceQueueCreateInfo)); + + uint32_t *queue_indices = xcalloc(QUEUE_FAMILY_COUNT, sizeof(uint32_t)); + queue_indices[0] = indices.graphics_family; + queue_indices[1] = indices.present_family; + + float queue_priority = 1.0f; + for(int i = 0; i < QUEUE_FAMILY_COUNT; i++) + { + queue_infos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_infos[i].queueFamilyIndex = queue_indices[i]; + queue_infos[i].queueCount = 1; + queue_infos[i].pQueuePriorities = &queue_priority; + } + + // device features + VkPhysicalDeviceFeatures device_features = {0}; + + // logical device create info + VkDeviceCreateInfo create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + + create_info.pQueueCreateInfos = queue_infos; + create_info.queueCreateInfoCount = QUEUE_FAMILY_COUNT; + + create_info.pEnabledFeatures = &device_features; + + create_info.enabledExtensionCount = 0; + + // validation layers can be set, but + // newer implementations will ignore them + // so i wont bother adding them + create_info.enabledLayerCount = 0; + + if(vkCreateDevice(device->physical_device, &create_info, NULL, &device->logical_device) != VK_SUCCESS) { + err("vkCreateDevice: failed"); + goto fail; + } + + // vkGetDeviceQueue(device->logical_device, indices.graphics_family, 0, &device->graphics_queue); + + free(queue_infos); + free(queue_indices); + + return 0; +fail: + return 1; +} + +// returns a bitmask if a given family is set, or not +static int find_queue_families(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct queue_family_idx *queue_family) +{ + int ret = 0; + + uint32_t count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(phy_device, &count, NULL); + + VkQueueFamilyProperties *properties = xcalloc(count, sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties(phy_device, &count, properties); + + for(uint32_t i = 0; i < count; i++) { + if(properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) { + ret |= QUEUE_FAMILY_GRAPHICS_FAMILY; + if(queue_family != NULL) { + queue_family->graphics_family = i; + } + } + + VkBool32 present_support = 0; + vkGetPhysicalDeviceSurfaceSupportKHR(phy_device, i, surface, &present_support); + if(present_support) { + ret |= QUEUE_FAMILY_PRESENT_FAMILY; + if(queue_family != NULL){ + queue_family->present_family = i; + } + } + } + + free(properties); + return ret; +} + +static int is_device_suitable(VkPhysicalDevice phy_device, VkSurfaceKHR surface) +{ + if(find_queue_families(phy_device, surface, NULL) == QUEUE_FAMILY_ALL) { + return 1; + } + + return 0; +} + + +#ifdef DEBUG + +static int create_debug_messenger(device_t device, struct device_info *info) +{ + (void)info; + + VkDebugUtilsMessengerCreateInfoEXT cinfo = {0}; + populate_debug_messenger_info(&cinfo); + + if(CreateDebugUtilsMessengerEXT(device->instance, &cinfo, NULL, &device->debug_messenger) != VK_SUCCESS) { + info("CreateDebugUtilMessengerEXT: failed"); + return 1; + } + + return 0; +} + +static void populate_debug_messenger_info(VkDebugUtilsMessengerCreateInfoEXT *info) +{ + info->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + info->messageSeverity = + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + info->messageType = + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + info->pfnUserCallback = debug_callback; +} + +static int has_validation_layer_support(const char * const *layers, uint32_t nlayers) +{ + (void)layers; + (void)nlayers; + + // uint32_t navaliable = 0; + // vkEnumerateInstanceLayerProperties(&navaliable, NULL); + + // VkLayerProperties *available_layers = xcalloc(navaliable, sizeof(VkLayerProperties)); + // vkEnumerateInstanceLayerProperties(&navaliable, available_layers); + return 1; +} + +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) +{ + (void)messageSeverity; + (void)messageType; + (void)pUserData; + + warn("Validation Layer: %s", pCallbackData->pMessage); + return VK_FALSE; +} + +VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { + PFN_vkCreateDebugUtilsMessengerEXT f = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + if (f != NULL) { + return f(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { + PFN_vkDestroyDebugUtilsMessengerEXT f = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + if (f != NULL) { + f(instance, debugMessenger, pAllocator); + } +} + +#endif |