#include "gpu.h" #include #define VK_NO_PROTOTYPES #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #else #include #endif // State static struct { void* library; gpu_config config; } state; // Helpers #define MIN(a, b) (a < b ? a : b) #define MAX(a, b) (a > b ? a : b) #define COUNTOF(x) (sizeof(x) / sizeof(x[0])) #define VK(f, s) if (!vcheck(f, s)) #define CHECK(c, s) if (!check(c, s)) static VkBool32 relay(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags, const VkDebugUtilsMessengerCallbackDataEXT* data, void* userdata); static bool vcheck(VkResult result, const char* message); static bool check(bool condition, const char* message); // Loader // Functions that don't require an instance #define GPU_FOREACH_ANONYMOUS(X)\ X(vkCreateInstance) // Functions that require an instance but don't require a device #define GPU_FOREACH_INSTANCE(X)\ X(vkDestroyInstance)\ X(vkCreateDebugUtilsMessengerEXT)\ X(vkDestroyDebugUtilsMessengerEXT)\ X(vkDestroySurfaceKHR)\ X(vkEnumeratePhysicalDevices)\ X(vkGetPhysicalDeviceProperties2)\ X(vkGetPhysicalDeviceFeatures2)\ X(vkGetPhysicalDeviceMemoryProperties)\ X(vkGetPhysicalDeviceFormatProperties)\ X(vkGetPhysicalDeviceQueueFamilyProperties)\ X(vkGetPhysicalDeviceSurfaceSupportKHR)\ X(vkGetPhysicalDeviceSurfaceCapabilitiesKHR)\ X(vkGetPhysicalDeviceSurfaceFormatsKHR)\ X(vkCreateDevice)\ X(vkDestroyDevice)\ X(vkGetDeviceQueue)\ X(vkGetDeviceProcAddr) // Functions that require a device #define GPU_FOREACH_DEVICE(X)\ X(vkSetDebugUtilsObjectNameEXT)\ X(vkCmdBeginDebugUtilsLabelEXT)\ X(vkCmdEndDebugUtilsLabelEXT)\ X(vkDeviceWaitIdle)\ X(vkQueueSubmit)\ X(vkQueuePresentKHR)\ X(vkCreateSwapchainKHR)\ X(vkDestroySwapchainKHR)\ X(vkGetSwapchainImagesKHR)\ X(vkAcquireNextImageKHR)\ X(vkCreateCommandPool)\ X(vkDestroyCommandPool)\ X(vkResetCommandPool)\ X(vkAllocateCommandBuffers)\ X(vkBeginCommandBuffer)\ X(vkEndCommandBuffer)\ X(vkCreateFence)\ X(vkDestroyFence)\ X(vkResetFences)\ X(vkGetFenceStatus)\ X(vkWaitForFences)\ X(vkCreateSemaphore)\ X(vkDestroySemaphore)\ X(vkCmdPipelineBarrier)\ X(vkCreateQueryPool)\ X(vkDestroyQueryPool)\ X(vkCmdResetQueryPool)\ X(vkCmdWriteTimestamp)\ X(vkCmdCopyQueryPoolResults)\ X(vkCreateBuffer)\ X(vkDestroyBuffer)\ X(vkGetBufferMemoryRequirements)\ X(vkBindBufferMemory)\ X(vkCreateImage)\ X(vkDestroyImage)\ X(vkGetImageMemoryRequirements)\ X(vkBindImageMemory)\ X(vkCmdCopyBuffer)\ X(vkCmdCopyImage)\ X(vkCmdBlitImage)\ X(vkCmdCopyBufferToImage)\ X(vkCmdCopyImageToBuffer)\ X(vkCmdFillBuffer)\ X(vkCmdClearColorImage)\ X(vkAllocateMemory)\ X(vkFreeMemory)\ X(vkMapMemory)\ X(vkCreateSampler)\ X(vkDestroySampler)\ X(vkCreateRenderPass)\ X(vkDestroyRenderPass)\ X(vkCmdBeginRenderPass)\ X(vkCmdEndRenderPass)\ X(vkCreateImageView)\ X(vkDestroyImageView)\ X(vkCreateFramebuffer)\ X(vkDestroyFramebuffer)\ X(vkCreateShaderModule)\ X(vkDestroyShaderModule)\ X(vkCreateDescriptorSetLayout)\ X(vkDestroyDescriptorSetLayout)\ X(vkCreatePipelineLayout)\ X(vkDestroyPipelineLayout)\ X(vkCreateDescriptorPool)\ X(vkDestroyDescriptorPool)\ X(vkAllocateDescriptorSets)\ X(vkResetDescriptorPool)\ X(vkUpdateDescriptorSets)\ X(vkCreatePipelineCache)\ X(vkDestroyPipelineCache)\ X(vkCreateGraphicsPipelines)\ X(vkCreateComputePipelines)\ X(vkDestroyPipeline)\ X(vkCmdSetViewport)\ X(vkCmdSetScissor)\ X(vkCmdPushConstants)\ X(vkCmdBindPipeline)\ X(vkCmdBindDescriptorSets)\ X(vkCmdBindVertexBuffers)\ X(vkCmdBindIndexBuffer)\ X(vkCmdDraw)\ X(vkCmdDrawIndexed)\ X(vkCmdDrawIndirect)\ X(vkCmdDrawIndexedIndirect)\ X(vkCmdDispatch)\ X(vkCmdDispatchIndirect) // Used to load/declare Vulkan functions without lots of clutter #define GPU_LOAD_ANONYMOUS(fn) fn = (PFN_##fn) vkGetInstanceProcAddr(NULL, #fn); #define GPU_LOAD_INSTANCE(fn) fn = (PFN_##fn) vkGetInstanceProcAddr(state.instance, #fn); #define GPU_LOAD_DEVICE(fn) fn = (PFN_##fn) vkGetDeviceProcAddr(state.device, #fn); #define GPU_DECLARE(fn) static PFN_##fn fn; // Declare function pointers GPU_FOREACH_ANONYMOUS(GPU_DECLARE) GPU_FOREACH_INSTANCE(GPU_DECLARE) GPU_FOREACH_DEVICE(GPU_DECLARE) // Entry bool gpu_init(gpu_config* config) { state.config = *config; // Load #ifdef _WIN32 state.library = LoadLibraryA("vulkan-1.dll"); CHECK(state.library, "Failed to load vulkan library") return gpu_destroy(), false; PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(state.library, "vkGetInstanceProcAddr"); #elif __APPLE__ state.library = dlopen("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL); CHECK(state.library, "Failed to load vulkan library") return gpu_destroy(), false; PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) dlsym(state.library, "vkGetInstanceProcAddr"); #else state.library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); CHECK(state.library, "Failed to load vulkan library") return gpu_destroy(), false; PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) dlsym(state.library, "vkGetInstanceProcAddr"); #endif GPU_FOREACH_ANONYMOUS(GPU_LOAD_ANONYMOUS); return true; } void gpu_destroy(void) { #ifdef _WIN32 if (state.library) FreeLibrary(state.library); #else if (state.library) dlclose(state.library); #endif memset(&state, 0, sizeof(state)); } // Helpers static VkBool32 relay(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags, const VkDebugUtilsMessengerCallbackDataEXT* data, void* userdata) { if (state.config.callback) { bool severe = severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; state.config.callback(state.config.userdata, data->pMessage, severe); } return VK_FALSE; } static bool vcheck(VkResult result, const char* message) { if (result >= 0) return true; if (!state.config.callback) return false; #define CASE(x) case x: state.config.callback(state.config.userdata, "Vulkan error: " #x, false); break; switch (result) { CASE(VK_ERROR_OUT_OF_HOST_MEMORY); CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY); CASE(VK_ERROR_INITIALIZATION_FAILED); CASE(VK_ERROR_DEVICE_LOST); CASE(VK_ERROR_MEMORY_MAP_FAILED); CASE(VK_ERROR_LAYER_NOT_PRESENT); CASE(VK_ERROR_EXTENSION_NOT_PRESENT); CASE(VK_ERROR_FEATURE_NOT_PRESENT); CASE(VK_ERROR_TOO_MANY_OBJECTS); CASE(VK_ERROR_FORMAT_NOT_SUPPORTED); CASE(VK_ERROR_FRAGMENTED_POOL); CASE(VK_ERROR_OUT_OF_POOL_MEMORY); default: break; } #undef CASE state.config.callback(state.config.userdata, message, true); return false; } static bool check(bool condition, const char* message) { if (!condition && state.config.callback) { state.config.callback(state.config.userdata, message, true); } return condition; }