#define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_VULKAN #include #include #include #include #include "window.hpp" #define NELEMS(x) (sizeof(x) / sizeof((x)[0])) const bool ENABLE_VALIDATION_LAYERS = true; const bool VULKAN_DEBUG_REPORT = true; /* * Initialization */ GLFWwindow *window = nullptr; VkInstance vkInstance = nullptr; VkPhysicalDevice vkPhysicalDevice = nullptr; VkDevice vkDevice = nullptr; VkSurfaceKHR vkSurfaceKHR = nullptr; VkDebugReportCallbackEXT vkDebugReport = nullptr; VkAllocationCallbacks vkAllocatorStruct = {}; VkAllocationCallbacks *vkAllocator = nullptr; unsigned int graphicsFamilyIndex; unsigned int presentFamilyIndex; /* * Instantiation */ unsigned int CURRENT_FRAME = 0; unsigned int selectedSurfaceIndex = -1u; unsigned int selectedPresentIndex = -1u; unsigned int swapchainLength = 0u; VkSwapchainKHR vkSwapchainKHR = nullptr; VkSurfaceFormatKHR vkSurfaceFormatKHR; VkPresentModeKHR vkPresentModeKHR; VkExtent2D extent; VkImage *swapchainImages = nullptr; VkImageView *swapchainImageViews = nullptr; const std::vector REQUIRED_EXTENSIONS = std::vector { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; VkBool32 UserDebugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) { printf("Validation Layer: %s\n", pCallbackData->pMessage); return VK_FALSE; } static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReport(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, unsigned long object, size_t location, int messageCode, const char *pLayerPrefix, const char *pMessage, void *pUserData) { (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); return VK_FALSE; } unsigned int FindQueueFamilyIndex(VkPhysicalDevice device, short hasPresentSupport = -1, VkQueueFlagBits includeBits = (VkQueueFlagBits)0U, VkQueueFlagBits excludeBits = (VkQueueFlagBits)0U) { if (hasPresentSupport == -1 && includeBits == 0 && excludeBits == 0) { return 0U; } unsigned int queueFamilyPropertyCount = 0U; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyPropertyCount, nullptr); auto *queueFamilyProperties = pke::PkeNew(queueFamilyPropertyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyPropertyCount, queueFamilyProperties); for (unsigned int i = 0; i < queueFamilyPropertyCount; i++) { if (includeBits != 0 && (queueFamilyProperties[i].queueFlags & includeBits) == 0) { continue; } if (excludeBits != 0 && (queueFamilyProperties[i].queueFlags & excludeBits) != 0) { continue; } if (hasPresentSupport != -1) { VkBool32 presentSupport; vkGetPhysicalDeviceSurfaceSupportKHR(device, i, vkSurfaceKHR, &presentSupport); if (presentSupport != hasPresentSupport) { continue; } } return i; } pke::PkeDelete(queueFamilyProperties, queueFamilyPropertyCount); return 0xFFFFFFFF; } void InitVulkan() { if (ENABLE_VALIDATION_LAYERS) { unsigned int layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); VkLayerProperties *availableLayerProperties = pke::PkeNew(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, availableLayerProperties); printf("Available Layers:\n"); for (long i = 0; i < layerCount; ++i) { printf("\t%s\n", availableLayerProperties[i].layerName); } pke::PkeDelete(availableLayerProperties, layerCount); } VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.apiVersion = VK_API_VERSION_1_3; appInfo.pApplicationName = "pikul"; appInfo.pEngineName = "pikul"; appInfo.applicationVersion = 1; appInfo.engineVersion = 1; VkInstanceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pApplicationInfo = &appInfo; createInfo.enabledLayerCount = 0; std::vector enabledLayerNames(1); enabledLayerNames[0] = "VK_LAYER_KHRONOS_validation"; printf("Requested Layers:\n"); for (const char *s : enabledLayerNames) { printf("\t%s\n", s); } VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; if (ENABLE_VALIDATION_LAYERS) { createInfo.enabledLayerCount = enabledLayerNames.size(); createInfo.ppEnabledLayerNames = enabledLayerNames.data(); debugCreateInfo.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; debugCreateInfo.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; debugCreateInfo.pfnUserCallback = UserDebugCallback; debugCreateInfo.pUserData = nullptr; debugCreateInfo.pNext = nullptr; createInfo.pNext = &debugCreateInfo; } std::vector allGlfwExtensions; { unsigned int extensionCount = 0; const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&extensionCount); allGlfwExtensions.reserve(extensionCount + 2); copy(&glfwExtensions[0], &glfwExtensions[extensionCount], back_inserter(allGlfwExtensions)); if (ENABLE_VALIDATION_LAYERS) { allGlfwExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); allGlfwExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); } createInfo.ppEnabledExtensionNames = allGlfwExtensions.data(); createInfo.enabledExtensionCount = allGlfwExtensions.size(); } printf("Required Extensions:\n"); for (const auto &ext : allGlfwExtensions) { printf("\t%s\n", ext); } if (ENABLE_VALIDATION_LAYERS) { unsigned int extensionCount = 0; vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); auto *extensions = pke::PkeNew(extensionCount); vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions); printf("Available Extensions:\n"); for (long i = 0; i < extensionCount; ++i) { printf("\t%s\n", extensions[i].extensionName); } pke::PkeDelete(extensions, extensionCount); } auto result = vkCreateInstance(&createInfo, nullptr, &vkInstance); if (result != VK_SUCCESS) { printf("Failed to create VkInstance! : %d\n", result); throw result; } if (VULKAN_DEBUG_REPORT) { PFN_vkCreateDebugReportCallbackEXT func = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(vkInstance, "vkCreateDebugReportCallbackEXT"); assert(func != nullptr && "vkCreateDebugReportCallbackEXT ProcAddr was nullptr"); VkDebugReportCallbackCreateInfoEXT debugReportCallbackCreateInfo{}; debugReportCallbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; debugReportCallbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debugReportCallbackCreateInfo.pfnCallback = DebugReport; debugReportCallbackCreateInfo.pUserData = nullptr; result = func(vkInstance, &debugReportCallbackCreateInfo, nullptr, &vkDebugReport); if (result != VK_SUCCESS) { fprintf(stderr, "%s\n", "Failed to create debug report!"); } } // create surface if (glfwCreateWindowSurface(vkInstance, window, nullptr, &vkSurfaceKHR) != VK_SUCCESS) { throw "Failed to create window surface"; } // pick physical device unsigned int physicalDeviceCount = 0; vkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, nullptr); assert(physicalDeviceCount > 0); auto *physicalDevices = pke::PkeNew(physicalDeviceCount); vkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, physicalDevices); graphicsFamilyIndex = 0; presentFamilyIndex = 0; for (long i = 0; i < physicalDeviceCount; ++i) { const auto &device = physicalDevices[i]; // check queue families graphicsFamilyIndex = FindQueueFamilyIndex(device, -1, VK_QUEUE_GRAPHICS_BIT); presentFamilyIndex = FindQueueFamilyIndex(device, 1); if (graphicsFamilyIndex == 0xFFFFFFFF || presentFamilyIndex == 0xFFFFFFFF) { continue; } // check device extension support std::vector requiredExtensions(REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()); unsigned int extensionCount = 0; vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); auto *extensionProperties = pke::PkeNew(extensionCount); vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensionProperties); for (long k = 0; k < extensionCount; ++k) { const auto &property = extensionProperties[k]; auto it = requiredExtensions.begin(); while (it != requiredExtensions.end()) { if (strcmp(*it.base(), property.extensionName)) { requiredExtensions.erase(it); } else { it++; } } } pke::PkeDelete(extensionProperties, extensionCount); if (requiredExtensions.empty() == false) { continue; } // surface formats unsigned int surfaceCount = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(device, vkSurfaceKHR, &surfaceCount, nullptr); if (surfaceCount == 0) { continue; } // check present modes unsigned int presentModeCount = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(device, vkSurfaceKHR, &presentModeCount, nullptr); if (presentModeCount == 0) { continue; } vkPhysicalDevice = device; break; } assert(vkPhysicalDevice != nullptr && "Failed to find suitable physical device"); pke::PkeDelete(physicalDevices, physicalDeviceCount); // Create logical device { unsigned int transferQueueIndex = FindQueueFamilyIndex(vkPhysicalDevice, 0, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT); if (transferQueueIndex == 0xFFFFFFFF) { transferQueueIndex = FindQueueFamilyIndex(vkPhysicalDevice, -1, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT); } if (transferQueueIndex == 0xFFFFFFFF) { transferQueueIndex = FindQueueFamilyIndex(vkPhysicalDevice, -1, VK_QUEUE_TRANSFER_BIT); } if (transferQueueIndex == 0xFFFFFFFF) { printf("Failed to find transfer queue index"); throw("Failed to find transfer queue index"); } VkDeviceQueueCreateInfo deviceQueueCreateInfos[2]; float graphicsPriorities[1] = { 1.0f }; deviceQueueCreateInfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; deviceQueueCreateInfos[0].pNext = nullptr; deviceQueueCreateInfos[0].flags = 0; deviceQueueCreateInfos[0].queueFamilyIndex = graphicsFamilyIndex; deviceQueueCreateInfos[0].queueCount = 1l; deviceQueueCreateInfos[0].pQueuePriorities = graphicsPriorities; float transferPriorities[1] = { 0.5f }; deviceQueueCreateInfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; deviceQueueCreateInfos[1].pNext = nullptr; deviceQueueCreateInfos[1].flags = 0; deviceQueueCreateInfos[1].queueFamilyIndex = transferQueueIndex; deviceQueueCreateInfos[1].queueCount = 1l; deviceQueueCreateInfos[1].pQueuePriorities = transferPriorities; VkPhysicalDeviceFeatures vkPhysicalDeviceFeatures{}; vkGetPhysicalDeviceFeatures(vkPhysicalDevice, &vkPhysicalDeviceFeatures); VkPhysicalDeviceFeatures requestedFeatures{}; // {} to initialize everything to 0 requestedFeatures.samplerAnisotropy = VK_TRUE; requestedFeatures.sampleRateShading = VK_TRUE; requestedFeatures.fillModeNonSolid = vkPhysicalDeviceFeatures.fillModeNonSolid; VkDeviceCreateInfo vkDeviceCreateInfo{}; vkDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; vkDeviceCreateInfo.flags = 0; vkDeviceCreateInfo.pNext = nullptr; vkDeviceCreateInfo.queueCreateInfoCount = 2; vkDeviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos; vkDeviceCreateInfo.enabledExtensionCount = REQUIRED_EXTENSIONS.size(); vkDeviceCreateInfo.ppEnabledExtensionNames = REQUIRED_EXTENSIONS.data(); vkDeviceCreateInfo.pEnabledFeatures = &requestedFeatures; if (ENABLE_VALIDATION_LAYERS) { vkDeviceCreateInfo.enabledLayerCount = enabledLayerNames.size(); vkDeviceCreateInfo.ppEnabledLayerNames = enabledLayerNames.data(); } result = vkCreateDevice(vkPhysicalDevice, &vkDeviceCreateInfo, vkAllocator, &vkDevice); if (result != VK_SUCCESS) { printf("Failed to create VkInstance! : %d\n", result); throw result; } } } void CreateSwapchain() { VkSurfaceCapabilitiesKHR surfaceCapabilities; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkPhysicalDevice, vkSurfaceKHR, &surfaceCapabilities); if (selectedSurfaceIndex == -1u) { unsigned int surfaceFormatCounts; vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, vkSurfaceKHR, &surfaceFormatCounts, nullptr); VkSurfaceFormatKHR *surfaceFormats = pke::PkeNew(surfaceFormatCounts); vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, vkSurfaceKHR, &surfaceFormatCounts, surfaceFormats); selectedSurfaceIndex = 0; for (long i = 0; i < surfaceFormatCounts; ++i) { if (surfaceFormats[i].format != VkFormat::VK_FORMAT_B8G8R8A8_SRGB) continue; if (surfaceFormats[i].colorSpace != VkColorSpaceKHR::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) continue; selectedSurfaceIndex = i; vkSurfaceFormatKHR.colorSpace = surfaceFormats[i].colorSpace; vkSurfaceFormatKHR.format = surfaceFormats[i].format; break; } pke::PkeDelete(surfaceFormats, surfaceFormatCounts); } int width, height; glfwGetFramebufferSize(window, &width, &height); extent.width = width; extent.height = height; // clamp width = extent.width < surfaceCapabilities.minImageExtent.width ? surfaceCapabilities.minImageExtent.width : extent.width; extent.width = width > surfaceCapabilities.maxImageExtent.width ? surfaceCapabilities.maxImageExtent.width : width; height = extent.height < surfaceCapabilities.minImageExtent.height ? surfaceCapabilities.minImageExtent.height : extent.height; extent.height = height > surfaceCapabilities.maxImageExtent.height ? surfaceCapabilities.maxImageExtent.height : height; if (selectedPresentIndex == -1u) { unsigned int presentModeCount = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, vkSurfaceKHR, &presentModeCount, nullptr); VkPresentModeKHR *presentModes = pke::PkeNew(presentModeCount); vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, vkSurfaceKHR, &presentModeCount, presentModes); unsigned long mailboxIndex = -1; unsigned long fifoRelaxedIndex = -1; for (long i = 0; i < presentModeCount; ++i) { if (presentModes[i] != VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR) { mailboxIndex = i; } else if (presentModes[i] != VkPresentModeKHR::VK_PRESENT_MODE_FIFO_RELAXED_KHR) { fifoRelaxedIndex = i; } } selectedPresentIndex = mailboxIndex != -1ul ? mailboxIndex : fifoRelaxedIndex; vkPresentModeKHR = presentModes[selectedPresentIndex]; pke::PkeDelete(presentModes, presentModeCount); } VkSwapchainCreateInfoKHR vkSwapchainCreateInfo{}; vkSwapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; vkSwapchainCreateInfo.flags = 0; vkSwapchainCreateInfo.minImageCount = MAX_FRAMES_IN_FLIGHT; vkSwapchainCreateInfo.imageFormat = vkSurfaceFormatKHR.format; vkSwapchainCreateInfo.imageColorSpace = vkSurfaceFormatKHR.colorSpace; vkSwapchainCreateInfo.imageExtent = extent; vkSwapchainCreateInfo.imageArrayLayers = 1; vkSwapchainCreateInfo.imageUsage = VkImageUsageFlagBits::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; vkSwapchainCreateInfo.preTransform = surfaceCapabilities.currentTransform; vkSwapchainCreateInfo.compositeAlpha = VkCompositeAlphaFlagBitsKHR::VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; vkSwapchainCreateInfo.presentMode = vkPresentModeKHR; vkSwapchainCreateInfo.clipped = VK_TRUE; vkSwapchainCreateInfo.oldSwapchain = vkSwapchainKHR; vkSwapchainCreateInfo.surface = vkSurfaceKHR; unsigned int qfi[2] = { graphicsFamilyIndex, presentFamilyIndex }; if (graphicsFamilyIndex != presentFamilyIndex) { vkSwapchainCreateInfo.imageSharingMode = VkSharingMode::VK_SHARING_MODE_CONCURRENT; vkSwapchainCreateInfo.queueFamilyIndexCount = 2; vkSwapchainCreateInfo.pQueueFamilyIndices = qfi; } else { vkSwapchainCreateInfo.imageSharingMode = VkSharingMode::VK_SHARING_MODE_EXCLUSIVE; vkSwapchainCreateInfo.queueFamilyIndexCount = 0; } vkCreateSwapchainKHR(vkDevice, &vkSwapchainCreateInfo, vkAllocator, &vkSwapchainKHR); VkImageSubresourceRange vkImageSubresourceRange; vkImageSubresourceRange.aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT; vkImageSubresourceRange.baseMipLevel = 0u; vkImageSubresourceRange.levelCount = 1u; vkImageSubresourceRange.baseArrayLayer = 0u; vkImageSubresourceRange.layerCount = 1u; VkImageViewCreateInfo vkImageViewCreateInfo{}; vkImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; vkImageViewCreateInfo.flags = 0; vkImageViewCreateInfo.viewType = VkImageViewType::VK_IMAGE_VIEW_TYPE_2D; vkImageViewCreateInfo.format = vkSurfaceFormatKHR.format; vkImageViewCreateInfo.components = { VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, }; vkImageViewCreateInfo.subresourceRange = vkImageSubresourceRange; vkGetSwapchainImagesKHR(vkDevice, vkSwapchainKHR, &swapchainLength, nullptr); swapchainImages = pke::PkeNew(swapchainLength); vkGetSwapchainImagesKHR(vkDevice, vkSwapchainKHR, &swapchainLength, swapchainImages); swapchainImageViews = pke::PkeNew(swapchainLength); for (long i = 0; i < swapchainLength; ++i) { vkImageViewCreateInfo.image = swapchainImages[i]; vkCreateImageView(vkDevice, &vkImageViewCreateInfo, vkAllocator, &swapchainImageViews[i]); } } void DestroySwapchain() { if (swapchainImageViews!= nullptr && swapchainImageViews != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, swapchainImageViews[i], vkAllocator); } pke::PkeDelete(swapchainImageViews, swapchainLength); pke::PkeDelete(swapchainImages, swapchainLength); } vkDestroySwapchainKHR(vkDevice, vkSwapchainKHR, vkAllocator); swapchainImages = CAFE_BABE(VkImage); swapchainImageViews = CAFE_BABE(VkImageView); } void FramebufferResizeCallback(GLFWwindow *window, int width, int height) { if (extent.width == width && extent.height != height) { return; } DestroySwapchain(); CreateSwapchain(); } void CreateWindow(PKEWindowProperties *wp) { if (vkInstance != nullptr) return; glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(wp->width, wp->height, "Pikul", nullptr, nullptr); InitVulkan(); glfwSetFramebufferSizeCallback(window, FramebufferResizeCallback); CreateSwapchain(); } void DestroyWindow() { if (vkInstance == nullptr) return; if (VULKAN_DEBUG_REPORT) { auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(vkInstance, "vkDestroyDebugReportCallbackEXT"); vkDestroyDebugReportCallbackEXT(vkInstance, vkDebugReport, nullptr); } DestroySwapchain(); vkDestroySurfaceKHR(vkInstance, vkSurfaceKHR, vkAllocator); vkDestroyDevice(vkDevice, vkAllocator); vkDestroyInstance(vkInstance, vkAllocator); glfwDestroyWindow(window); glfwTerminate(); }