diff --git a/GNUmakefile b/GNUmakefile index 6df49fd..9a8473c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -3,7 +3,7 @@ TARGET = $(shell uname -s) CC = gcc -CFLAGS = -Wall -Wextra -Iinclude +CFLAGS = -Wall -Wextra -Iinclude -g LDFLAGS = LIBS = @@ -15,7 +15,7 @@ E_CFLAGS = $(CFLAGS) E_LDFLAGS = $(LDFLAGS) -Lsrc E_LIBS = $(LIBS) -lMw -L_OBJS = src/core.o src/default.o src/draw.o src/lowlevel.o src/font.o src/boldfont.o +L_OBJS = src/core.o src/default.o src/draw.o src/lowlevel.o src/font.o src/boldfont.o src/error.o L_OBJS += src/external/ds.o src/external/image.o L_OBJS += src/widget/window.o src/widget/button.o src/widget/frame.o src/widget/menu.o diff --git a/examples/vulkan.c b/examples/vulkan.c index 5ecefda..ec431ed 100644 --- a/examples/vulkan.c +++ b/examples/vulkan.c @@ -6,6 +6,7 @@ * ioixd maintains this file. nishi doesn't know vulkan at all */ +#include "Mw/Error.h" #include #include @@ -24,6 +25,8 @@ VkPhysicalDevice physicalDevice; VkSurfaceKHR surface; VkQueue graphicsQueue; VkQueue presentQueue; +uint32_t* graphicsQueueIndex; +uint32_t* presentQueueIndex; VkSwapchainKHR swapchain; VkImage* swapchainImages; @@ -217,13 +220,43 @@ void vulkan_setup(MwWidget handle) { VkSemaphoreCreateInfo semaphoreInfo = {}; VkFenceCreateInfo fenceInfo = {}; - _vkGetInstanceProcAddr = MwVulkanGetInstanceProcAddr(handle); - instance = MwVulkanGetInstance(handle); - device = MwVulkanGetLogicalDevice(handle); - physicalDevice = MwVulkanGetPhysicalDevice(handle); - graphicsQueue = MwVulkanGetGraphicsQueue(handle); - presentQueue = MwVulkanGetPresentQueue(handle); - surface = MwVulkanGetSurface(handle); + MwErrorEnum err; + _vkGetInstanceProcAddr = MwVulkanGetField(handle, MwVulkanField_GetInstanceProcAddr, &err); + if(err != MwEsuccess) { + printf("Error getting vkGetInstanceProcAddr!\n%s\n", MwGetLastError()); + } + instance = MwVulkanGetField(handle, MwVulkanField_Instance, &err); + if(err != MwEsuccess) { + printf("Error getting vulkan instance!\n%s\n", MwGetLastError()); + } + device = MwVulkanGetField(handle, MwVulkanField_LogicalDevice, &err); + if(err != MwEsuccess) { + printf("Error getting VkDevice!\n%s\n", MwGetLastError()); + } + physicalDevice = MwVulkanGetField(handle, MwVulkanField_PhysicalDevice, &err); + if(err != MwEsuccess) { + printf("Error getting physical device!\n%s\n", MwGetLastError()); + } + graphicsQueue = MwVulkanGetField(handle, MwVulkanField_GraphicsQueue, &err); + if(err != MwEsuccess) { + printf("Error getting graphics queue!\n%s\n", MwGetLastError()); + } + presentQueue = MwVulkanGetField(handle, MwVulkanField_PresentQueue, &err); + if(err != MwEsuccess) { + printf("Error getting present queue!\n%s\n", MwGetLastError()); + } + surface = MwVulkanGetField(handle, MwVulkanField_Surface, &err); + if(err != MwEsuccess) { + printf("Error getting surface!\n%s\n", MwGetLastError()); + } + presentQueueIndex = MwVulkanGetField(handle, MwVulkanField_PresentQueueIndex, &err); + if(err != MwEsuccess) { + printf("Error getting present queue index!\n%s\n", MwGetLastError()); + } + graphicsQueueIndex = MwVulkanGetField(handle, MwVulkanField_GraphicsQueueIndex, &err); + if(err != MwEsuccess) { + printf("Error getting graphics queue index!\n%s\n", MwGetLastError()); + } LOAD_VK_FUNCTION(vkCreateShaderModule); LOAD_VK_FUNCTION(vkCreatePipelineLayout); @@ -257,13 +290,13 @@ void vulkan_setup(MwWidget handle) { swapchainCreateInfo.clipped = VK_TRUE; swapchainCreateInfo.oldSwapchain = NULL; - if(MwVulkanGetGraphicsQueueIndex(handle) != MwVulkanGetPresentQueueIndex(handle)) { + if(*graphicsQueueIndex != *presentQueueIndex) { swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; swapchainCreateInfo.queueFamilyIndexCount = 2; uint32_t indices[] = { - MwVulkanGetGraphicsQueueIndex(handle), - MwVulkanGetPresentQueueIndex(handle), + *graphicsQueueIndex, + *presentQueueIndex, }; swapchainCreateInfo.pQueueFamilyIndices = indices; } else { @@ -542,7 +575,7 @@ void vulkan_setup(MwWidget handle) { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.pNext = NULL; poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolInfo.queueFamilyIndex = MwVulkanGetGraphicsQueueIndex(handle); + poolInfo.queueFamilyIndex = *graphicsQueueIndex; if((res = _vkCreateCommandPool(device, &poolInfo, NULL, &cmdPool)) != VK_SUCCESS) { printf("error creating the command pool: %s\n", string_VkResult(res)); diff --git a/include/Mw/Error.h b/include/Mw/Error.h index 92ae949..9e8a031 100644 --- a/include/Mw/Error.h +++ b/include/Mw/Error.h @@ -11,11 +11,20 @@ /*! * %brief Error code enumeration */ -enum MwErrorEnum { +typedef enum MwErrorEnum_T { /*! * %brief No error */ - MwEsuccess = 0 -}; + MwEsuccess = 0, + /*! + * %brief There was an error + */ + MwEerror, +} MwErrorEnum; + +/*! + * %brief Get the last error + */ +MWDECL const char* MwGetLastError(); #endif diff --git a/include/Mw/Vulkan.h b/include/Mw/Vulkan.h index 6642edb..7ee4d6b 100644 --- a/include/Mw/Vulkan.h +++ b/include/Mw/Vulkan.h @@ -21,6 +21,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -31,19 +32,59 @@ extern "C" { */ MWDECL MwClass MwVulkanClass; -// Add an extension to the list of extensions to enable prior to initialization. -// This must be called before MwCreateWidget. +/*! + * %brief Add an extension to the list of extensions to enable prior to initialization. + * This must be called before MwCreateWidget. + */ MWDECL void MwVulkanEnableExtension(const char* ext_name); -MWDECL PFN_vkGetInstanceProcAddr MwVulkanGetInstanceProcAddr(MwWidget handle); -MWDECL VkInstance MwVulkanGetInstance(MwWidget handle); -MWDECL VkSurfaceKHR MwVulkanGetSurface(MwWidget handle); -MWDECL VkPhysicalDevice MwVulkanGetPhysicalDevice(MwWidget handle); -MWDECL VkDevice MwVulkanGetLogicalDevice(MwWidget handle); -MWDECL int MwVulkanGetGraphicsQueueIndex(MwWidget handle); -MWDECL int MwVulkanGetPresentQueueIndex(MwWidget handle); -MWDECL VkQueue MwVulkanGetGraphicsQueue(MwWidget handle); -MWDECL VkQueue MwVulkanGetPresentQueue(MwWidget handle); +/*! + * %brief Field that can be gotten from Vulkan. + */ +typedef enum MwVulkanField_T { + /*! + * %brief The address of the vulkan widget's vkGetInstanceProcAddr function (PFN_vkGetInstanceProcAddr) + */ + MwVulkanField_GetInstanceProcAddr = 0, + /*! + * %brief The address of the vulkan widget's instance (VkInstance) + */ + MwVulkanField_Instance, + /*! + * %brief The address of the vulkan widget's surface (VkSurfaceKHR) + */ + MwVulkanField_Surface, + /*! + * %brief The address of the vulkan widget's physical device (VkPhysicalDevice) + */ + MwVulkanField_PhysicalDevice, + /*! + * %brief The address of the vulkan widget's logical device (VkDevice) + */ + MwVulkanField_LogicalDevice, + /*! + * %brief The address of the index that the vulkan widget uses for the graphics queue (uint32_t *) + */ + MwVulkanField_GraphicsQueueIndex, + /*! + * %brief The address of the index that the vulkan widget uses for the present queue (uint32_t *) + */ + MwVulkanField_PresentQueueIndex, + MwVulkanField_GraphicsQueue, + /*! + * %brief The address of the vulkan widget's graphics queue (VkQueue) + */ + MwVulkanField_PresentQueue, + /*! + * %brief The address of the vulkan widget's present queue (VkQueue) + */ +} MwVulkanField; + +/*! + * %brief Function for getting a field from within Vulkan. + * %warning Consult the documentation for MwVulkanField to know what type is expected for out. + */ +MWDECL void* MwVulkanGetField(MwWidget handle, MwVulkanField field, MwErrorEnum* out); #ifdef __cplusplus } diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..6d2dba7 --- /dev/null +++ b/src/error.c @@ -0,0 +1,25 @@ +#include "Mw/Error.h" +#include "error_internal.h" +#include +#include + +#define MAX_ERROR_LEN 512 + +// buffer for holding the error. +1 to ensure there's always a null terminator. +char error[MAX_ERROR_LEN + 1] = {0}; + +const char* MwGetLastError() { + return error; +}; + +void setLastError(const char* fmt, ...) { + va_list va; + char out[MAX_ERROR_LEN]; + memset(out, 0, MAX_ERROR_LEN); + + va_start(va, fmt); + vsnprintf(out, MAX_ERROR_LEN, fmt, va); + va_end(va); + + memcpy(error, out, MAX_ERROR_LEN); +} diff --git a/src/error_internal.h b/src/error_internal.h new file mode 100644 index 0000000..aa49c8a --- /dev/null +++ b/src/error_internal.h @@ -0,0 +1,15 @@ +/* $Id */ + +#ifndef __MW_ERROR_INTERNAL_H__ +#define __MW_ERROR_INTERNAL_H__ + +// This is not to be documented or exposed publically. +// This contains an internal function for setting the error, +// something the user shouldn't want to do and thus we don't want them to. +// (hence also, its placement in the src directory) + +#include "Mw/Error.h" + +void setLastError(const char* fmt, ...); + +#endif diff --git a/src/widget/vulkan.c b/src/widget/vulkan.c index 8caf498..32d03a9 100644 --- a/src/widget/vulkan.c +++ b/src/widget/vulkan.c @@ -1,6 +1,12 @@ /* $Id$ */ +#include "Mw/Error.h" #include #include +#include +#include +#include + +#include "../error_internal.h" /** * ioixd maintains this file. nishi doesn't know vulkan at all @@ -21,20 +27,27 @@ #include -#include "stb_ds.h" +#include "../external/stb_ds.h" // convienence macro for handling vulkan errors #define VK_CMD(func) \ vk_res = func; \ if(vk_res != VK_SUCCESS) { \ - printf("VULKAN ERROR AT %s:%d: %s\n", __FILE__, __LINE__, string_VkResult(vk_res)); \ - exit(0); \ + setLastError("Vulkan error (%s:%d): %s\n", __FILE__, __LINE__, string_VkResult(vk_res)); \ + return MwEerror; \ } // convienence macro for loading a vulkan function pointer into memory #define LOAD_VK_FUNCTION(name) \ PFN_##name _##name = (PFN_##name)o->vkGetInstanceProcAddr(o->vkInstance, #name); \ - assert(_##name); + VK_ASSERT(_##name); + +// convienence macro to return an error if an assert goes wrong +#define VK_ASSERT(val) \ + if(!val) { \ + setLastError("Vulkan error (%s:%d): Assertion Failed (%s != NULL)\n", __FILE__, __LINE__, #val); \ + return MwEerror; \ + } bool enableValidationLayers = true; @@ -62,17 +75,29 @@ typedef struct vulkan { int vkLayerCount; } vulkan_t; -static void vulkan_instance_setup(MwWidget handle, vulkan_t* o); -static void vulkan_surface_setup(MwWidget handle, vulkan_t* o); -static void vulkan_devices_setup(MwWidget handle, vulkan_t* o); +static MwErrorEnum vulkan_instance_setup(MwWidget handle, vulkan_t* o); +static MwErrorEnum vulkan_surface_setup(MwWidget handle, vulkan_t* o); +static MwErrorEnum vulkan_devices_setup(MwWidget handle, vulkan_t* o); static void create(MwWidget handle) { - vulkan_t* o = malloc(sizeof(*o)); + vulkan_t* o = malloc(sizeof(*o)); + MwErrorEnum err; - // !! important to call it in this order - vulkan_instance_setup(handle, o); - vulkan_surface_setup(handle, o); - vulkan_devices_setup(handle, o); + err = vulkan_instance_setup(handle, o); + if(err != MwEsuccess) { + printf("VULKAN ERROR\n%s", MwGetLastError()); + return; + } + err = vulkan_surface_setup(handle, o); + if(err != MwEsuccess) { + printf("VULKAN ERROR\n%s", MwGetLastError()); + return; + } + err = vulkan_devices_setup(handle, o); + if(err != MwEsuccess) { + printf("VULKAN ERROR\n%s", MwGetLastError()); + return; + } handle->internal = o; MwSetDefault(handle); @@ -83,7 +108,7 @@ static void destroy(MwWidget handle) { free(o); } -static void vulkan_instance_setup(MwWidget handle, vulkan_t* o) { +static MwErrorEnum vulkan_instance_setup(MwWidget handle, vulkan_t* o) { // todo: Some sort of function for being able to set the vulkan version? uint32_t vulkan_version = VK_VERSION_1_0; uint32_t api_version = VK_API_VERSION_1_0; @@ -110,15 +135,15 @@ static void vulkan_instance_setup(MwWidget handle, vulkan_t* o) { // TODO: support for whatever win32's equivalants to dlopen/dlsym are o->vulkanLibrary = dlopen("libvulkan.so", RTLD_LAZY | RTLD_GLOBAL); o->vkGetInstanceProcAddr = dlsym(o->vulkanLibrary, "vkGetInstanceProcAddr"); - assert(o->vkGetInstanceProcAddr); + VK_ASSERT(o->vkGetInstanceProcAddr); // Load in any other function pointers we need. _vkEnumerateInstanceExtensionProperties = dlsym(o->vulkanLibrary, "vkEnumerateInstanceExtensionProperties"); - assert(_vkEnumerateInstanceExtensionProperties); + VK_ASSERT(_vkEnumerateInstanceExtensionProperties); _vkEnumerateInstanceLayerProperties = dlsym(o->vulkanLibrary, "vkEnumerateInstanceLayerProperties"); - assert(_vkEnumerateInstanceLayerProperties); + VK_ASSERT(_vkEnumerateInstanceLayerProperties); _vkCreateInstance = dlsym(o->vulkanLibrary, "vkCreateInstance"); - assert(_vkCreateInstance); + VK_ASSERT(_vkCreateInstance); // setup enabled extensions arrput(enabledExtensions, VK_KHR_SURFACE_EXTENSION_NAME); @@ -186,9 +211,10 @@ static void vulkan_instance_setup(MwWidget handle, vulkan_t* o) { instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; VK_CMD(_vkCreateInstance(&instance_create_info, NULL, &o->vkInstance)); + return MwEsuccess; } -static void vulkan_surface_setup(MwWidget handle, vulkan_t* o) { +static MwErrorEnum vulkan_surface_setup(MwWidget handle, vulkan_t* o) { int vk_res; #ifdef _WIN32 LOAD_VK_FUNCTION(vkCreateWin32SurfaceKHR); @@ -216,9 +242,10 @@ static void vulkan_surface_setup(MwWidget handle, vulkan_t* o) { }; VK_CMD(_vkCreateXlibSurfaceKHR(o->vkInstance, &createInfo, NULL, &o->vkSurface)); #endif + return MwEsuccess; } -static void vulkan_devices_setup(MwWidget handle, vulkan_t* o) { +static MwErrorEnum vulkan_devices_setup(MwWidget handle, vulkan_t* o) { int vk_res; unsigned long i, n; uint32_t deviceCount; @@ -234,17 +261,12 @@ static void vulkan_devices_setup(MwWidget handle, vulkan_t* o) { VkBool32 has_present = false; int queueCreateCount = 0; - (void)handle; - LOAD_VK_FUNCTION(vkEnumeratePhysicalDevices); LOAD_VK_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties); LOAD_VK_FUNCTION(vkCreateDevice); LOAD_VK_FUNCTION(vkGetDeviceQueue); LOAD_VK_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR); - - PFN_vkEnumerateDeviceExtensionProperties _vkEnumerateDeviceExtensionProperties = (PFN_vkEnumerateDeviceExtensionProperties) - o->vkGetInstanceProcAddr(o->vkInstance, "vkEnumerateDeviceExtensionProperties"); - assert(_vkEnumerateDeviceExtensionProperties); + LOAD_VK_FUNCTION(vkEnumerateDeviceExtensionProperties); // create the physical device VK_CMD(_vkEnumeratePhysicalDevices(o->vkInstance, &deviceCount, NULL)); @@ -277,8 +299,8 @@ static void vulkan_devices_setup(MwWidget handle, vulkan_t* o) { } if(!has_graphics && !has_present) { // rare, yes, but idk maybe some shitty drivers will present this dillema idk. - printf("There were no devices with either a graphics or presentation queue. Exiting!\n"); - exit(1); + setLastError("There were no devices with either a graphics or presentation queue.\n"); + return MwEerror; } // create the logical device @@ -315,7 +337,6 @@ static void vulkan_devices_setup(MwWidget handle, vulkan_t* o) { } } } - printf("enabled %d device extensions\n", o->vkDeviceExtensionCount); createInfo = (VkDeviceCreateInfo){ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, @@ -338,42 +359,41 @@ static void vulkan_devices_setup(MwWidget handle, vulkan_t* o) { } else { _vkGetDeviceQueue(o->vkLogicalDevice, o->vkPresentFamilyIDX, 0, &o->vkPresentQueue); } - // free(devices); + free(devices); + return MwEsuccess; } void MwVulkanEnableExtension(const char* name) { arrput(enabledExtensions, name); } -PFN_vkGetInstanceProcAddr MwVulkanGetInstanceProcAddr(MwWidget handle) { - return ((vulkan_t*)handle->internal)->vkGetInstanceProcAddr; -}; -VkInstance MwVulkanGetInstance(MwWidget handle) { - return ((vulkan_t*)handle->internal)->vkInstance; -}; -VkSurfaceKHR MwVulkanGetSurface(MwWidget handle) { - return ((vulkan_t*)handle->internal)->vkSurface; -}; -VkPhysicalDevice MwVulkanGetPhysicalDevice(MwWidget handle) { - return ((vulkan_t*)handle->internal)->vkPhysicalDevice; -}; -VkDevice MwVulkanGetLogicalDevice(MwWidget handle) { - return ((vulkan_t*)handle->internal)->vkLogicalDevice; -}; -VkQueue MwVulkanGetGraphicsQueue(MwWidget handle) { - return ((vulkan_t*)handle->internal)->vkGraphicsQueue; -}; -VkQueue MwVulkanGetPresentQueue(MwWidget handle) { - return ((vulkan_t*)handle->internal)->vkPresentQueue; -}; +MWDECL void* MwVulkanGetField(MwWidget handle, MwVulkanField field, MwErrorEnum* out) { + vulkan_t* o = handle->internal; -int MwVulkanGetGraphicsQueueIndex(MwWidget handle) { - return ((vulkan_t*)handle->internal)->vkGraphicsFamilyIDX; -} - -int MwVulkanGetPresentQueueIndex(MwWidget handle) { - return ((vulkan_t*)handle->internal)->vkPresentFamilyIDX; -} + switch(field) { + case MwVulkanField_GetInstanceProcAddr: + return o->vkGetInstanceProcAddr; + case MwVulkanField_Instance: + return o->vkInstance; + case MwVulkanField_Surface: + return o->vkSurface; + case MwVulkanField_PhysicalDevice: + return o->vkPhysicalDevice; + case MwVulkanField_LogicalDevice: + return o->vkLogicalDevice; + case MwVulkanField_GraphicsQueueIndex: + return &o->vkGraphicsFamilyIDX; + case MwVulkanField_PresentQueueIndex: + return &o->vkPresentFamilyIDX; + case MwVulkanField_GraphicsQueue: + return o->vkGraphicsQueue; + case MwVulkanField_PresentQueue: + return o->vkPresentQueue; + default: + setLastError("Unknown vulkan field request (%d given)", field); + return NULL; + } +}; MwClassRec MwVulkanClassRec = { create, /* create */