#include "core/VulkanBase.hpp" #include #include "GLFW.hpp" import Core.List; import Core.Logger; import Core.Types; import Core.Array; using Core::Vulkan::Base; #define GET_FUNCTION(name) (reinterpret_cast(getFunction(#name))) Base::Base() : instance(VK_NULL_HANDLE), #ifdef DEBUG_VULKAN debugMessenger(VK_NULL_HANDLE), debugReportCallback(VK_NULL_HANDLE), #endif surface(VK_NULL_HANDLE), physicalDevice(VK_NULL_HANDLE), device(VK_NULL_HANDLE) { } Base::~Base() { destroy(); } bool Base::initInstance() { u32 baseCount = 0; const char** baseExtensions = glfwGetRequiredInstanceExtensions(&baseCount); if(baseExtensions == nullptr) { VK_REPORT_ERROR("Could not get required extensions from GLFW"); return true; } Core::List extensions; for(u32 i = 0; i < baseCount; i++) { extensions.add(baseExtensions[i]); } Core::List layers; #ifdef DEBUG_VULKAN extensions.add(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); extensions.add(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); layers.add("VK_LAYER_KHRONOS_validation"); #endif VkApplicationInfo appInfo = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "Vulkan", .applicationVersion = VK_MAKE_VERSION(1, 0, 0), .pEngineName = "Kajetan", .engineVersion = VK_MAKE_VERSION(0, 0, 1), .apiVersion = VK_API_VERSION_1_1}; VkInstanceCreateInfo info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &appInfo, .enabledLayerCount = static_cast(layers.getLength()), .ppEnabledLayerNames = &layers[0], .enabledExtensionCount = static_cast(extensions.getLength()), .ppEnabledExtensionNames = &extensions[0]}; VK_CHECK_TRUE(vkCreateInstance(&info, nullptr, &instance)); return false; } #ifdef DEBUG_VULKAN static VKAPI_ATTR VkBool32 onVulkanDebugMessenger VKAPI_CALL( VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT* data, void*) { VK_REPORT_WARNING("Vulkan validation layer message: {}", data->pMessage); return false; } bool Base::initDebugMessenger() { VkDebugUtilsMessengerCreateInfoEXT info = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, .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, .pfnUserCallback = onVulkanDebugMessenger}; auto f = GET_FUNCTION(vkCreateDebugUtilsMessengerEXT); if(f == nullptr) { VK_REPORT_WARNING("Could not find debug util messenger function"); return false; } VK_CHECK_TRUE(f(instance, &info, nullptr, &debugMessenger)); return false; } static VKAPI_ATTR VkBool32 onVulkanDebugReport VKAPI_CALL( VkDebugReportFlagsEXT, VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t, const char* pLayerPrefix, const char* pMessage, void*) { VK_REPORT_WARNING("Vulkan debug message '{}': {}", pLayerPrefix, pMessage); return false; } bool Base::initDebugReportCallback() { VkDebugReportCallbackCreateInfoEXT info = { .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT, .flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, .pfnCallback = onVulkanDebugReport}; auto f = GET_FUNCTION(vkCreateDebugReportCallbackEXT); if(f == nullptr) { VK_REPORT_WARNING("Could not find debug report function"); return false; } VK_CHECK_TRUE(f(instance, &info, nullptr, &debugReportCallback)); return false; } void Base::destroyDebugMessenger() { if(debugMessenger == VK_NULL_HANDLE) { return; } auto f = GET_FUNCTION(vkDestroyDebugUtilsMessengerEXT); if(f == nullptr) { VK_REPORT_WARNING( "Could not find debug util messenger destroy function"); return; } f(instance, debugMessenger, nullptr); debugMessenger = VK_NULL_HANDLE; } void Base::destroyDebugReportCallback() { if(debugReportCallback == VK_NULL_HANDLE) { return; } auto f = GET_FUNCTION(vkDestroyDebugReportCallbackEXT); if(f == nullptr) { VK_REPORT_WARNING("Could not find debug report destroy function"); return; } f(instance, debugReportCallback, nullptr); debugReportCallback = VK_NULL_HANDLE; } #endif bool Base::initSurface() { GLFWwindow* w = Window::get(); if(w == nullptr) { VK_REPORT_ERROR("Init a window before Vulkan"); return true; } VK_CHECK_TRUE(glfwCreateWindowSurface(instance, w, nullptr, &surface)); return false; } bool Base::init() { if(initInstance()) { return true; } #ifdef DEBUG_VULKAN if(initDebugMessenger() || initDebugReportCallback()) { return true; } #endif return initSurface(); } void Base::destroy() { #ifdef DEBUG_VULKAN destroyDebugMessenger(); destroyDebugReportCallback(); #endif if(instance != VK_NULL_HANDLE) { vkDestroySurfaceKHR(instance, surface, nullptr); surface = VK_NULL_HANDLE; vkDestroyDevice(device, nullptr); device = VK_NULL_HANDLE; } vkDestroyInstance(instance, nullptr); instance = VK_NULL_HANDLE; } PFN_vkVoidFunction Base::getFunction(const char* name) { return vkGetInstanceProcAddr(instance, name); } void Base::getPhysicalDeviceProperties(VkPhysicalDeviceProperties& p) { vkGetPhysicalDeviceProperties(physicalDevice, &p); } u32 Base::findQueueFamily(VkQueueFlags flags) { Core::Array p; u32 c = p.getLength(); vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &c, p.begin()); for(u32 i = 0; i < c; i++) { if((p[i].queueFlags & flags) == flags) { return i; } } return INVALID_QUEUE_FAMILY; } static bool hasPresentationSupport( VkPhysicalDevice pd, VkSurfaceKHR s, u32 index) { VkBool32 b = false; VK_CHECK(false, vkGetPhysicalDeviceSurfaceSupportKHR(pd, index, s, &b)); return b; } u32 Base::findSurfaceQueueFamily() { u32 c = 0; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &c, nullptr); for(u32 i = 0; i < c; i++) { if(hasPresentationSupport(physicalDevice, surface, i)) { return i; } } return INVALID_QUEUE_FAMILY; } bool Base::hasExtension(const char* extension) { Core::Array e; u32 c = e.getLength(); VK_CHECK( false, vkEnumerateDeviceExtensionProperties( physicalDevice, nullptr, &c, &e[0])); for(u32 i = 0; i < c; i++) { if(strcmp(e[i].extensionName, extension) == 0) { return true; } } return false; } bool Base::getSurfaceCapabilities(VkSurfaceCapabilitiesKHR& c) { VK_CHECK_TRUE( vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &c)); return false; } bool Base::findSurfaceFormat( VkSurfaceFormatKHR& sf, SurfaceFormatSelector sfs) { Core::Array formats; u32 c = formats.getLength(); VK_CHECK_TRUE(vkGetPhysicalDeviceSurfaceFormatsKHR( physicalDevice, surface, &c, formats.begin())); int bestPoints = 0; for(u32 i = 0; i < c; i++) { int points = sfs(formats[i]); if(points > bestPoints) { bestPoints = points; sf = formats[i]; } } return bestPoints == 0; } bool Base::findSurfacePresentMode( VkPresentModeKHR& m, SurfacePresentModeSelector spms) { Core::Array modes; u32 c = modes.getLength(); VK_CHECK_TRUE(vkGetPhysicalDeviceSurfacePresentModesKHR( physicalDevice, surface, &c, modes.begin())); int bestPoints = 0; for(u32 i = 0; i < c; i++) { int points = spms(modes[i]); if(points > bestPoints) { bestPoints = points; m = modes[i]; } } return bestPoints == 0; } bool Base::initDevice( const List data, const List& extensions) { Core::List qInfo; for(const DeviceQueueData& d : data) { VkDeviceQueueCreateInfo& i = qInfo.put(); i.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; i.queueFamilyIndex = d.queueFamilyIndex; i.queueCount = 1; i.pQueuePriorities = &d.priority; } VkPhysicalDeviceFeatures deviceFeatures = {}; vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); VkDeviceCreateInfo info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .queueCreateInfoCount = static_cast(qInfo.getLength()), .pQueueCreateInfos = &qInfo[0], .enabledExtensionCount = static_cast(extensions.getLength()), .ppEnabledExtensionNames = &extensions[0], .pEnabledFeatures = &deviceFeatures}; VK_CHECK_TRUE(vkCreateDevice(physicalDevice, &info, nullptr, &device)); return false; } void Base::waitForIdle() { if(device != VK_NULL_HANDLE) { vkDeviceWaitIdle(device); } } u32 Base::findMemoryType(u32 typeFilter, VkMemoryPropertyFlags flags) { VkPhysicalDeviceMemoryProperties m; vkGetPhysicalDeviceMemoryProperties(physicalDevice, &m); for(u32 i = 0; i < m.memoryTypeCount; i++) { if(typeFilter & (1u << i) && (m.memoryTypes[i].propertyFlags & flags) == flags) { return i; } } return INVALID_MEMORY_TYPE; }