From 5e910c7e41d18dab95e34ceb6e620a8958197ae6 Mon Sep 17 00:00:00 2001 From: kartofen Date: Mon, 16 Oct 2023 23:46:15 +0300 Subject: things done --- src/common.c | 33 +++++ src/common.h | 34 +++++ src/device.c | 395 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device.h | 33 +++++ src/main.c | 88 +++++++++++++ src/window.c | 36 ++++++ src/window.h | 23 ++++ 7 files changed, 642 insertions(+) create mode 100644 src/common.c create mode 100644 src/common.h create mode 100644 src/device.c create mode 100644 src/device.h create mode 100644 src/main.c create mode 100644 src/window.c create mode 100644 src/window.h (limited to 'src') diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..084c584 --- /dev/null +++ b/src/common.c @@ -0,0 +1,33 @@ +#include +#include +#include "common.h" + +void *xmalloc(size_t size) +{ + void *ret; + if(!(ret = malloc(size))) { + die("malloc: %s", strerror(errno)); + } + + return ret; +} + +void *xcalloc(size_t nmemb, size_t size) +{ + void *ret; + if(!(ret = calloc(nmemb, size))) { + die("calloc: %s", strerror(errno)); + } + + return ret; +} + +void *xrealloc(void *ptr, size_t size) +{ + void *ret; + if(!(ret = realloc(ptr, size))) { + die("realloc: %s", strerror(errno)); + } + + return ret; +} diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..e10649a --- /dev/null +++ b/src/common.h @@ -0,0 +1,34 @@ +#ifndef LOG_H +#define LOG_H + +#include +#include +#include + +#define ARR_SIZE(a) (sizeof(a)/sizeof(*(a))) + +#define __RED__ "\033[0;31m" +#define __GREEN__ "\033[0;32m" +#define __YELLOW__ "\033[0;33m" +#define __RESET__ "\033[0m" + +#define STR(x) #x +#define XSTR(x) STR(x) + +#define info(...) do { fprintf(stdout, __GREEN__"[INFO]"__RESET__" "__VA_ARGS__); fprintf(stdout, "\n"); } while(0) +#define err(...) do { fprintf(stderr, __RED__"[ERROR]"__RESET__" "__FILE__":"XSTR(__LINE__)": "__VA_ARGS__); fprintf(stderr, "\n"); } while(0) +#define warn(...) do { fprintf(stderr, __YELLOW__"[WARN]"__RESET__" "__FILE__":"XSTR(__LINE__)": "__VA_ARGS__); fprintf(stderr, "\n"); } while(0) + +#define die(...) do { \ + err(__VA_ARGS__); \ + abort(); \ + } while(0) + +#define MAKE_VERSION(major, minor, patch) \ + ((((uint32_t)(major)) << 22U) | (((uint32_t)(minor)) << 12U) | ((uint32_t)(patch))) + +void *xmalloc(size_t size); +void *xcalloc(size_t nmemb, size_t size); +void *xrealloc(void *ptr, size_t size); + +#endif 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 +#include + +#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 diff --git a/src/device.h b/src/device.h new file mode 100644 index 0000000..d6768e1 --- /dev/null +++ b/src/device.h @@ -0,0 +1,33 @@ +#ifndef DEVICE_H +#define DEVICE_H + +#include +#include "common.h" + +typedef struct device { + VkInstance instance; + VkDebugUtilsMessengerEXT debug_messenger; + + VkPhysicalDevice physical_device; + VkDevice logical_device; + VkQueue graphics_queue; + + VkSurfaceKHR surface; +} * device_t; + +typedef int (*surface_func_t)(VkInstance instance, VkSurfaceKHR *surface); + +struct device_info { + char *name; + uint32_t version; + + const char* const* extensions; + uint32_t ext_count; + + surface_func_t surface_func; +}; + +device_t device_create(struct device_info *info); +void device_destroy(device_t device); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..90985be --- /dev/null +++ b/src/main.c @@ -0,0 +1,88 @@ +#include +#include +#include + +#include "device.h" +#include "window.h" +#include "common.h" + +// TODO: rename device.c/.h +// TODO: fix fail gotos + +window_t window; +device_t device; + +int _create_surface(VkInstance instance, VkSurfaceKHR *surface); + +int main(void) +{ + int ret = 1; + + if(SDL_Init(0)) { + err("SDL_Init: failed"); + goto sf; + } + + // populate window info + struct window_info win_info = {0}; + win_info.title = "Test App"; + win_info.w = 1200; + win_info.h = 800; + + // create window + window = window_create(&win_info); + if(!window) { + err("window_create: failed"); + goto wf; + } + + // get extensions + unsigned int ext_count = 0; + window_extension_info(window, &ext_count, NULL); + + // populate the device info + struct device_info dev_info = {0}; + dev_info.name = "Test App"; + dev_info.version = MAKE_VERSION(1, 0, 0); + + if(ext_count == 0){ + dev_info.ext_count = 0; + dev_info.extensions = NULL; + } else { + char **extensions = xcalloc(ext_count, sizeof(char *)); + window_extension_info(window, &ext_count, (const char **)extensions); + + dev_info.ext_count = ext_count; + dev_info.extensions = (const char * const *)extensions; + } + + dev_info.surface_func = _create_surface; + + // create the device + device = device_create(&dev_info); + if(!device) { + err("device_create: failed"); + goto df; + } + + int running = 1; + while(running) { + SDL_Event windowEvent; + while(SDL_PollEvent(&windowEvent)) + if(windowEvent.type == SDL_QUIT) { + running = 0; + break; + } + } + + ret = 0; +df: device_destroy(device); +wf: window_destroy(window); +sf: SDL_Quit(); + return ret; +} + +int _create_surface(VkInstance instance, VkSurfaceKHR *surface) +{ + return window_create_surface(window, instance, surface); +} diff --git a/src/window.c b/src/window.c new file mode 100644 index 0000000..4a498e6 --- /dev/null +++ b/src/window.c @@ -0,0 +1,36 @@ +#include "window.h" +#include "common.h" + +window_t window_create(struct window_info *info) +{ + info->flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN; + SDL_Vulkan_LoadLibrary(NULL); + + window_t window = SDL_CreateWindow(info->title, info->x, info->y, + info->w, info->h, info->flags); + return window; +} + +void window_destroy(window_t window) +{ + SDL_DestroyWindow((SDL_Window *)window); + SDL_Vulkan_UnloadLibrary(); +} + +int window_extension_info(window_t window, unsigned int *ext_count, const char **extensions) +{ + if(SDL_Vulkan_GetInstanceExtensions((SDL_Window *)window, ext_count, extensions) == SDL_FALSE) { + return 1; + } + + return 0; +} + +int window_create_surface(window_t window, VkInstance instance, VkSurfaceKHR *surface) +{ + if(SDL_Vulkan_CreateSurface((SDL_Window *)window, instance, surface) == SDL_FALSE) { + return 1; + } + + return 0; +} diff --git a/src/window.h b/src/window.h new file mode 100644 index 0000000..01ecbb3 --- /dev/null +++ b/src/window.h @@ -0,0 +1,23 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include +#include +#include + +typedef SDL_Window * window_t; + +struct window_info { + char *title; + int x; int y; + int w; int h; + Uint32 flags; +}; + +window_t window_create(struct window_info *info); +void window_destroy(window_t window); + +int window_extension_info(window_t window, unsigned int *ext_count, const char **extensions); +int window_create_surface(window_t window, VkInstance instance, VkSurfaceKHR *surface); + +#endif -- cgit v1.2.3