summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorkartofen <mladenovnasko0@gmail.com>2023-10-16 23:46:15 +0300
committerkartofen <mladenovnasko0@gmail.com>2023-10-16 23:46:15 +0300
commit5e910c7e41d18dab95e34ceb6e620a8958197ae6 (patch)
treebf49c8ca6dd123d0765beef63d5858445dcf24aa /src
things done
Diffstat (limited to 'src')
-rw-r--r--src/common.c33
-rw-r--r--src/common.h34
-rw-r--r--src/device.c395
-rw-r--r--src/device.h33
-rw-r--r--src/main.c88
-rw-r--r--src/window.c36
-rw-r--r--src/window.h23
7 files changed, 642 insertions, 0 deletions
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 <string.h>
+#include <errno.h>
+#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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#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 <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
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 <vulkan/vulkan.h>
+#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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#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 <SDL2/SDL.h>
+#include <SDL2/SDL_vulkan.h>
+#include <vulkan/vulkan.h>
+
+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