diff --git a/src/main.c b/src/main.c index a45c2c5..0d68a26 100644 --- a/src/main.c +++ b/src/main.c @@ -1,1172 +1,5 @@ -#include "dyn_arr.h" -#include -#include -#include -#include -#include +#include "vulkan_wrapper.h" #include -#include -#include -#include - -#define GLFW_INCLUDE_VULKAN -#include - -#ifdef NDEBUG -const bool enableValidationLayers = false; -#else -const bool enableValidationLayers = true; -#endif - -#define MAX_FRAMES_IN_FLIGHT 2 - -static VKAPI_ATTR VkBool32 VKAPI_CALL -debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, - void *pUserData) { - // TODO: Put all messages into a log file - if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { - fprintf(stderr, "validation layer: %s\n", pCallbackData->pMessage); - } - - return VK_FALSE; -} - -const char *deviceExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; -size_t deviceExtentionCount = - sizeof(deviceExtensions) / sizeof(deviceExtensions[0]); - -const char *validationLayers[] = {"VK_LAYER_KHRONOS_validation"}; -size_t validationLayerCount = - sizeof(validationLayers) / sizeof(validationLayers[0]); - -static bool framebufferResized; // initialised to 0 - -typedef struct Application { - GLFWwindow *window; - VkInstance instance; - VkDebugUtilsMessengerEXT debugMessenger; - VkSurfaceKHR surface; - VkPhysicalDevice physicalDevice; - VkDevice device; - VkQueue graphicsQueue; - VkQueue presentQueue; - VkSwapchainKHR swapChain; - VkImage *swapChainImages; - uint32_t swapChainImageCount; - VkFormat swapChainImageFormat; - VkExtent2D swapChainExtent; - VkImageView *swapChainImageViews; - uint32_t swapChainImageViewCount; - VkRenderPass renderPass; - VkPipelineLayout pipelineLayout; - VkPipeline graphicsPipeline; - VkFramebuffer *swapChainFramebuffers; - uint32_t swapChainFramebufferCount; - VkCommandPool commandPool; - VkCommandBuffer commandBuffers[MAX_FRAMES_IN_FLIGHT]; - VkSemaphore imageAvailableSemaphores[MAX_FRAMES_IN_FLIGHT]; - VkSemaphore renderFinishedSemaphore[MAX_FRAMES_IN_FLIGHT]; - VkFence inFlightFences[MAX_FRAMES_IN_FLIGHT]; - uint32_t currentFrame; // initialised to 0 -} Application; - -typedef struct SwapChainSupportDetails { - VkSurfaceCapabilitiesKHR capabilities; - uint32_t numFormats; - VkSurfaceFormatKHR *formats; - uint32_t numPresentModes; - VkPresentModeKHR *presentModes; -} SwapChainSupportDetails; - -bool checkValidationLayerSupport() { - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, NULL); - VkLayerProperties availableLayers[layerCount]; - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers); - - for (int i = 0; i < validationLayerCount; i++) { - bool layerFound = false; - for (int j = 0; j < layerCount; j++) { - if (strcmp(availableLayers[j].layerName, validationLayers[i]) == 0) { - layerFound = true; - break; - } - } - if (!layerFound) { - return false; - } - } - return true; -} - -bool verifyExtensionSupport(uint32_t extensionCount, - VkExtensionProperties *extentions, - uint32_t glfwExtensionCount, - const char **glfwExtensions) { - for (uint32_t i = 0; i < glfwExtensionCount; i++) { - bool layerFound = false; - for (uint32_t j = 0; j < extensionCount; j++) { - if (strcmp(glfwExtensions[i], extentions[j].extensionName) == 0) { - layerFound = true; - break; - } - } - if (!layerFound) { - fprintf(stderr, "Missing %s vulkan extention\n", glfwExtensions[i]); - return false; - } - } - return true; -} - -void populateDebugMessengerCreateInfo( - VkDebugUtilsMessengerCreateInfoEXT *createInfo) { - createInfo->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - createInfo->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; - createInfo->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; - createInfo->pfnUserCallback = debugCallback; -} - -void createInstance(Application *app) { - if (enableValidationLayers && !checkValidationLayerSupport()) { - fprintf(stderr, "Validation layers requested, but not available!\n"); - exit(EXIT_FAILURE); - } - - VkApplicationInfo appInfo = { - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pApplicationName = "Hello Triangle", - .applicationVersion = VK_MAKE_VERSION(1, 0, 0), - .pEngineName = "No Engine", - .engineVersion = VK_MAKE_VERSION(1, 0, 0), - .apiVersion = VK_API_VERSION_1_0, - }; - - uint32_t glfwExtensionCount = 0; - const char **glfwExtensions; - glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - - const char *glfwExtensionsDebug[glfwExtensionCount + 1]; - - VkInstanceCreateInfo createInfo = { - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pApplicationInfo = &appInfo, - .enabledExtensionCount = glfwExtensionCount, - .ppEnabledExtensionNames = glfwExtensions, - .enabledLayerCount = 0, - }; - - uint32_t extensionCount = 0; - vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL); - VkExtensionProperties extensions[extensionCount]; - vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions); - - VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo = {0}; - if (enableValidationLayers) { - populateDebugMessengerCreateInfo(&debugCreateInfo); - createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo; - - createInfo.enabledLayerCount = validationLayerCount; - createInfo.ppEnabledLayerNames = validationLayers; - - for (size_t i = 0; i < glfwExtensionCount; i++) { - glfwExtensionsDebug[i] = glfwExtensions[i]; - } - glfwExtensionsDebug[glfwExtensionCount] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; - createInfo.enabledExtensionCount = glfwExtensionCount + 1; - createInfo.ppEnabledExtensionNames = glfwExtensionsDebug; - if (!verifyExtensionSupport(extensionCount, extensions, - glfwExtensionCount + 1, glfwExtensionsDebug)) { - fprintf( - stderr, - "Failed to find all required vulkan extentions for glfw and debug\n"); - exit(EXIT_FAILURE); - } - } else { - if (!verifyExtensionSupport(extensionCount, extensions, glfwExtensionCount, - glfwExtensions)) { - fprintf(stderr, - "Failed to find all required vulkan extentions for glfw\n"); - exit(EXIT_FAILURE); - } - } - - // This currently leaks memory on laptop :( - VkResult result = vkCreateInstance(&createInfo, NULL, &app->instance); - if (result != VK_SUCCESS) { - fprintf(stderr, "Failed to create vulkan instance\n"); - exit(EXIT_FAILURE); - } -} - -static void framebufferResizeCallback(GLFWwindow *window, int width, - int height) { - framebufferResized = true; -} - -void initWindow(Application *app) { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - app->window = glfwCreateWindow(800, 600, "Vulkan", NULL, NULL); - - glfwSetFramebufferSizeCallback(app->window, framebufferResizeCallback); -} - -VkResult CreateDebugUtilsMessengerEXT( - VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, - const VkAllocationCallbacks *pAllocator, - VkDebugUtilsMessengerEXT *pDebugMessenger) { - PFN_vkCreateDebugUtilsMessengerEXT func = - (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( - instance, "vkCreateDebugUtilsMessengerEXT"); - if (func != NULL) { - return func(instance, pCreateInfo, pAllocator, pDebugMessenger); - } else { - return VK_ERROR_EXTENSION_NOT_PRESENT; - } -} - -void setupDebugMessenger(Application *app) { - if (!enableValidationLayers) { - return; - } - if (!checkValidationLayerSupport()) { - fprintf(stderr, "Validation layers requested, but not available!\n"); - exit(EXIT_FAILURE); - } - VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; - populateDebugMessengerCreateInfo(&createInfo); - - if (CreateDebugUtilsMessengerEXT(app->instance, &createInfo, NULL, - &app->debugMessenger) != VK_SUCCESS) { - fprintf(stderr, "failed to set up debug messenger!\n"); - } -} - -void DestroyDebugUtilsMessengerEXT(VkInstance instance, - VkDebugUtilsMessengerEXT debugMessenger, - const VkAllocationCallbacks *pAllocator) { - PFN_vkDestroyDebugUtilsMessengerEXT func = - (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( - instance, "vkDestroyDebugUtilsMessengerEXT"); - if (func != NULL) { - func(instance, debugMessenger, pAllocator); - } -} - -struct QueueFamilyIndices_s { - bool graphicsFamilyExists; - uint32_t graphicsFamily; - bool presentFamilyExists; - uint32_t presentFamily; -} QueueFamilyIndices_default = { - .graphicsFamilyExists = false, - .graphicsFamily = 0, - .presentFamilyExists = false, - .presentFamily = 0, -}; -typedef struct QueueFamilyIndices_s QueueFamilyIndices; - -QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device, - VkSurfaceKHR *surface) { - QueueFamilyIndices indices = QueueFamilyIndices_default; - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, NULL); - VkQueueFamilyProperties queueFamilies[queueFamilyCount]; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, - queueFamilies); - for (uint32_t i = 0; i < queueFamilyCount; i++) { - // Graphics support - if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { - indices.graphicsFamily = i; - indices.graphicsFamilyExists = true; - } - // Surface support - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, *surface, &presentSupport); - if (presentSupport) { - indices.presentFamily = i; - indices.presentFamilyExists = true; - } - - // Early break if we've found everything - if (indices.presentFamilyExists && indices.graphicsFamilyExists) { - break; - } - } - - return indices; -} - -bool checkDeviceExtensionSupport(VkPhysicalDevice device) { - uint32_t availableExtensionCount; - vkEnumerateDeviceExtensionProperties(device, NULL, &availableExtensionCount, - NULL); - VkExtensionProperties availableExtentions[availableExtensionCount]; - vkEnumerateDeviceExtensionProperties(device, NULL, &availableExtensionCount, - availableExtentions); - - for (uint i = 0; i < deviceExtentionCount; i++) { - bool extentionFound = false; - for (uint j = 0; j < availableExtensionCount; j++) { - if (strcmp(availableExtentions[j].extensionName, deviceExtensions[i]) == - 0) { - extentionFound = true; - break; - } - } - if (!extentionFound) { - return false; - } - } - return true; -} - -// TODO: find a better way of managing memory -SwapChainSupportDetails querySwapchainSupport(VkPhysicalDevice device, - VkSurfaceKHR *surface) { - SwapChainSupportDetails details = {0}; - - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, *surface, - &details.capabilities); - - vkGetPhysicalDeviceSurfaceFormatsKHR(device, *surface, &details.numFormats, - NULL); - if (details.numFormats > 0) { - details.formats = malloc(details.numFormats * sizeof(VkSurfaceFormatKHR)); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, *surface, &details.numFormats, - details.formats); - } - - vkGetPhysicalDeviceSurfacePresentModesKHR(device, *surface, - &details.numPresentModes, NULL); - if (details.numPresentModes > 0) { - details.presentModes = - malloc(details.numPresentModes * sizeof(VkPresentModeKHR)); - vkGetPhysicalDeviceSurfacePresentModesKHR( - device, *surface, &details.numPresentModes, details.presentModes); - } - - return details; -} - -// Prefer one format but otherwise go for -VkSurfaceFormatKHR chooseSwapSurfaceFormat(VkSurfaceFormatKHR *availableFormats, - size_t availableFormatCount) { - for (uint i = 0; i < availableFormatCount; i++) { - if (availableFormats[i].format == VK_FORMAT_B8G8R8_SRGB && - availableFormats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - return availableFormats[i]; - } - } - return availableFormats[0]; -} - -// Prefer mailbox otherwise go for the garenteed available present mode -VkPresentModeKHR chooseSwapPresentMode(VkPresentModeKHR *availablePresentModes, - size_t availablePresentModesCount) { - for (uint i = 0; i < availablePresentModesCount; i++) { - if (availablePresentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { - return availablePresentModes[i]; - } - } - return VK_PRESENT_MODE_FIFO_KHR; -} - -VkExtent2D chooseSwapExtent(GLFWwindow *window, - const VkSurfaceCapabilitiesKHR *capabilities) { - if (capabilities->currentExtent.width != UINT_MAX) { - return capabilities->currentExtent; - } - - int width, height; - glfwGetFramebufferSize(window, &width, &height); - VkExtent2D actualExtent = {(uint32_t)width, (uint32_t)height}; - - // check width and height are within bounds - actualExtent.width = - fmin(capabilities->maxImageExtent.width, actualExtent.width); - actualExtent.width = - fmax(capabilities->minImageExtent.width, actualExtent.width); - actualExtent.height = - fmin(capabilities->maxImageExtent.height, actualExtent.height); - actualExtent.height = - fmax(capabilities->minImageExtent.height, actualExtent.height); - - return actualExtent; -} - -bool isDeviceSuitable(VkPhysicalDevice device, VkSurfaceKHR *surface) { - QueueFamilyIndices indices = findQueueFamilies(device, surface); - bool completeIndeces = - indices.graphicsFamilyExists && indices.presentFamilyExists; - - bool extensionsSupported = checkDeviceExtensionSupport(device); - - bool swapChainAdequate = false; - if (extensionsSupported) { - SwapChainSupportDetails swapChainDetails = - querySwapchainSupport(device, surface); - - if (swapChainDetails.numFormats > 0 && - swapChainDetails.numPresentModes > 0) { - swapChainAdequate = true; - } - - free(swapChainDetails.presentModes); - free(swapChainDetails.formats); - } - - return completeIndeces && extensionsSupported && swapChainAdequate; -} - -void pickPhysicalDevice(Application *app) { - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - - uint32_t physicalDeviceCount; - vkEnumeratePhysicalDevices(app->instance, &physicalDeviceCount, NULL); - if (physicalDeviceCount == 0) { - fprintf(stderr, "Failed to find GPU with vulkan support!\n"); - fprintf(stderr, "Failed to find GPU with vulkan support!\n"); - exit(EXIT_FAILURE); - } - VkPhysicalDevice physicalDevices[physicalDeviceCount]; - vkEnumeratePhysicalDevices(app->instance, &physicalDeviceCount, - physicalDevices); - - // TODO: pick device off of more than if it's just suitable - for (int i = 0; i < physicalDeviceCount; i++) { - if (isDeviceSuitable(physicalDevices[i], &app->surface)) { - physicalDevice = physicalDevices[i]; - break; - } - } - - if (physicalDevice == VK_NULL_HANDLE) { - fprintf(stderr, "Failed to find suitable GPU\n"); - exit(EXIT_FAILURE); - } - - app->physicalDevice = physicalDevice; -} - -void createLogicalDevice(Application *app) { - QueueFamilyIndices indices = - findQueueFamilies(app->physicalDevice, &app->surface); - - uint32_t queueFamilies[] = {indices.graphicsFamily, indices.presentFamily}; - size_t numQueues = sizeof(queueFamilies) / sizeof(uint32_t); - - // Make sure queue families don't have duplicates - // TODO: eventually move over to a set rather than a dynamic array - uint32_t *uniqueQueueFamilies = dyna_init(uint32_t); - for (uint i = 0; i < numQueues; i++) { - bool isUnique = true; - for (uint j = 0; j < dyna_length(uniqueQueueFamilies); j++) { - if (uniqueQueueFamilies[j] == queueFamilies[i]) { - isUnique = false; - break; - } - } - if (isUnique) { - dyna_append(uniqueQueueFamilies, queueFamilies[i]); - } - } - VkDeviceQueueCreateInfo queueCreateInfos[dyna_length(uniqueQueueFamilies)]; - - for (uint i = 0; i < dyna_length(uniqueQueueFamilies); i++) { - VkDeviceQueueCreateInfo queueCreateInfo = {}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = uniqueQueueFamilies[i]; - queueCreateInfo.queueCount = 1; - float queuePriority = 1.0f; - queueCreateInfo.pQueuePriorities = &queuePriority; - - queueCreateInfos[i] = queueCreateInfo; - } - - // Specify device features - VkPhysicalDeviceFeatures deviceFeatures = {VK_FALSE}; - - // Specify logical device - VkDeviceCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.pQueueCreateInfos = queueCreateInfos; - createInfo.queueCreateInfoCount = dyna_length(uniqueQueueFamilies); - createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtentionCount; - createInfo.ppEnabledExtensionNames = deviceExtensions; - - dyna_deinit(uniqueQueueFamilies); - - // Old Vulkan specify layers - createInfo.enabledLayerCount = 0; - createInfo.ppEnabledLayerNames = 0; - if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayerCount; - createInfo.ppEnabledLayerNames = validationLayers; - } - - if (vkCreateDevice(app->physicalDevice, &createInfo, NULL, &app->device) != - VK_SUCCESS) { - fprintf(stderr, "Failed to create logical device.\n"); - exit(EXIT_FAILURE); - } - - // Set graphics queue from logical device - vkGetDeviceQueue(app->device, indices.graphicsFamily, 0, &app->graphicsQueue); - vkGetDeviceQueue(app->device, indices.presentFamily, 0, &app->presentQueue); -} - -void createSurface(Application *app) { - if (glfwCreateWindowSurface(app->instance, app->window, NULL, - &app->surface) != VK_SUCCESS) { - fprintf(stderr, "Error creating vulkan surface in glfw window."); - exit(EXIT_FAILURE); - } -} -void createSwapChain(Application *app) { - SwapChainSupportDetails swapChainSupport = - querySwapchainSupport(app->physicalDevice, &app->surface); - VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat( - swapChainSupport.formats, swapChainSupport.numFormats); - VkPresentModeKHR presentMode = chooseSwapPresentMode( - swapChainSupport.presentModes, swapChainSupport.numPresentModes); - VkExtent2D extent = - chooseSwapExtent(app->window, &swapChainSupport.capabilities); - - uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; - if (swapChainSupport.capabilities.maxImageCount > 0 && - imageCount > swapChainSupport.capabilities.maxImageCount) { - imageCount = swapChainSupport.capabilities.maxImageCount; - } - - VkSwapchainCreateInfoKHR createInfo = {0}; - createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.surface = app->surface; - createInfo.minImageCount = imageCount; - createInfo.imageFormat = surfaceFormat.format; - createInfo.imageColorSpace = surfaceFormat.colorSpace; - createInfo.imageExtent = extent; - createInfo.imageArrayLayers = 1; - createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - - QueueFamilyIndices indices = - findQueueFamilies(app->physicalDevice, &app->surface); - uint32_t queueFamilyIndices[] = {indices.graphicsFamily, - indices.presentFamily}; - - if (indices.graphicsFamily != indices.presentFamily) { - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = 2; - createInfo.pQueueFamilyIndices = queueFamilyIndices; - } else { - createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.queueFamilyIndexCount = 0; - createInfo.pQueueFamilyIndices = NULL; - } - - createInfo.preTransform = swapChainSupport.capabilities.currentTransform; - createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - createInfo.presentMode = presentMode; - createInfo.clipped = VK_TRUE; - createInfo.oldSwapchain = VK_NULL_HANDLE; - - if (vkCreateSwapchainKHR(app->device, &createInfo, NULL, &app->swapChain) != - VK_SUCCESS) { - fprintf(stderr, "Failed to create swapchain!"); - exit(EXIT_FAILURE); - } - - vkGetSwapchainImagesKHR(app->device, app->swapChain, - &app->swapChainImageCount, NULL); - app->swapChainImages = malloc(app->swapChainImageCount * sizeof(VkImage)); - vkGetSwapchainImagesKHR(app->device, app->swapChain, - &app->swapChainImageCount, app->swapChainImages); - - app->swapChainImageFormat = surfaceFormat.format; - app->swapChainExtent = extent; - - free(swapChainSupport.presentModes); - free(swapChainSupport.formats); -} - -void createImageViews(Application *app) { - app->swapChainImageViewCount = app->swapChainImageCount; - app->swapChainImageViews = - malloc(app->swapChainImageViewCount * sizeof(VkImageView)); - - for (size_t i = 0; i < app->swapChainImageViewCount; i++) { - VkImageViewCreateInfo createInfo = {0}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = app->swapChainImages[i]; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = app->swapChainImageFormat; - - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - if (vkCreateImageView(app->device, &createInfo, NULL, - &app->swapChainImageViews[i]) != VK_SUCCESS) { - fprintf(stderr, "Failed to create image views!\n"); - exit(EXIT_FAILURE); - } - } -} - -// Buffer should be freed -typedef struct { - char *buffer; - size_t bufferSize; -} ShaderBuffer; - -ShaderBuffer loadShaderIntoBuffer(const char *fileLocation) { - ShaderBuffer buf = {.buffer = NULL, .bufferSize = 0}; - FILE *file = fopen(fileLocation, "r"); - - if (file == NULL) { - fprintf(stderr, "Could not open file \"%s\"!\n", fileLocation); - exit(EXIT_FAILURE); - } - - if (fseek(file, 0L, SEEK_END) != 0) { - fprintf(stderr, "Could not seek to the end of file \"%s\"!\n", - fileLocation); - exit(EXIT_FAILURE); - } - buf.bufferSize = ftell(file); - - if (fseek(file, 0L, SEEK_SET) != 0) { - fprintf(stderr, "Could not seek to the start of file \"%s\"!\n", - fileLocation); - exit(EXIT_FAILURE); - } - buf.buffer = - malloc(sizeof(char) * (buf.bufferSize)); // Not '\0' terminting ourselves - - size_t tempSize = fread(buf.buffer, sizeof(char), buf.bufferSize, file); - if (ferror(file) != 0) { - fprintf(stderr, "Error reading file into buffer \"%s\"!\n", fileLocation); - exit(EXIT_FAILURE); - } - fclose(file); - - return buf; -} - -VkShaderModule createShaderModule(VkDevice device, ShaderBuffer code) { - VkShaderModuleCreateInfo createInfo = {0}; - createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.codeSize = code.bufferSize; - createInfo.pCode = (uint32_t *)code.buffer; - - VkShaderModule shaderModule; - if (vkCreateShaderModule(device, &createInfo, NULL, &shaderModule) != - VK_SUCCESS) { - fprintf(stderr, "Failed to create shaderModule from Shader Code!"); - exit(EXIT_FAILURE); - } - - return shaderModule; -} - -void createGraphicsPipeline(Application *app) { - ShaderBuffer vertShaderCode = loadShaderIntoBuffer("shaders/shader.vert.spv"); - ShaderBuffer fragShaderCode = loadShaderIntoBuffer("shaders/shader.frag.spv"); - - VkShaderModule vertShaderModule = - createShaderModule(app->device, vertShaderCode); - VkShaderModule fragShaderModule = - createShaderModule(app->device, fragShaderCode); - - free(vertShaderCode.buffer); - free(fragShaderCode.buffer); - - VkPipelineShaderStageCreateInfo vertShaderStageInfo = {0}; - vertShaderStageInfo.sType = - VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertShaderStageInfo.module = vertShaderModule; - vertShaderStageInfo.pName = "main"; - vertShaderStageInfo.pSpecializationInfo = NULL; - - VkPipelineShaderStageCreateInfo fragShaderStageInfo = {0}; - fragShaderStageInfo.sType = - VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragShaderStageInfo.module = fragShaderModule; - fragShaderStageInfo.pName = "main"; - vertShaderStageInfo.pSpecializationInfo = NULL; - - VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, - fragShaderStageInfo}; - - size_t dynamicStatesCount = 2; - VkDynamicState dynamicStates[] = {VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR}; - VkPipelineDynamicStateCreateInfo dynamicState = {0}; - dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicState.dynamicStateCount = dynamicStatesCount; - dynamicState.pDynamicStates = dynamicStates; - - VkPipelineVertexInputStateCreateInfo vertexInputInfo = {0}; - vertexInputInfo.sType = - VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.pVertexBindingDescriptions = NULL; // Optional - vertexInputInfo.vertexAttributeDescriptionCount = 0; - vertexInputInfo.pVertexAttributeDescriptions = NULL; // Optional - - VkPipelineInputAssemblyStateCreateInfo inputAssembly = {0}; - inputAssembly.sType = - VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - VkViewport viewport = {0}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = (float)app->swapChainExtent.width; - viewport.height = (float)app->swapChainExtent.height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkPipelineViewportStateCreateInfo viewportState = {0}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.scissorCount = 1; - - VkPipelineRasterizationStateCreateInfo rasterizer = {0}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - rasterizer.depthBiasConstantFactor = 0.0f; // Optional - rasterizer.depthBiasClamp = 0.0f; // Optional - rasterizer.depthBiasSlopeFactor = 0.0f; // Optional - - // disable multisampling for now - VkPipelineMultisampleStateCreateInfo multisampling = {0}; - multisampling.sType = - VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisampling.minSampleShading = 1.0f; // Optional - multisampling.pSampleMask = NULL; // Optional - multisampling.alphaToCoverageEnable = VK_FALSE; // Optional - multisampling.alphaToOneEnable = VK_FALSE; // Optional - - VkPipelineColorBlendAttachmentState colorBlendAttachment = {0}; - colorBlendAttachment.colorWriteMask = - VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional - colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional - colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional - colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional - colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional - colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional - - VkPipelineColorBlendStateCreateInfo colorBlending = {0}; - colorBlending.sType = - VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {0}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 0; // Optional - pipelineLayoutInfo.pSetLayouts = NULL; // Optional - pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional - pipelineLayoutInfo.pPushConstantRanges = NULL; // Optional - - if (vkCreatePipelineLayout(app->device, &pipelineLayoutInfo, NULL, - &app->pipelineLayout) != VK_SUCCESS) { - fprintf(stderr, "Failed to create pipeline layout!"); - exit(EXIT_FAILURE); - } - - VkGraphicsPipelineCreateInfo pipelineInfo = {0}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = shaderStages; - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.pDynamicState = &dynamicState; - pipelineInfo.layout = app->pipelineLayout; - pipelineInfo.renderPass = app->renderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - - if (vkCreateGraphicsPipelines(app->device, VK_NULL_HANDLE, 1, &pipelineInfo, - NULL, &app->graphicsPipeline) != VK_SUCCESS) { - fprintf(stderr, "Failed to create graphics pipeline!"); - exit(EXIT_FAILURE); - } - - vkDestroyShaderModule(app->device, fragShaderModule, NULL); - vkDestroyShaderModule(app->device, vertShaderModule, NULL); -} - -void createRenderPass(Application *app) { - VkAttachmentDescription colorAttachment = {0}; - colorAttachment.format = app->swapChainImageFormat; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef = {0}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {0}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - VkSubpassDependency dependency = {0}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - VkRenderPassCreateInfo renderPassInfo = {0}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - renderPassInfo.dependencyCount = 1; - renderPassInfo.pDependencies = &dependency; - - if (vkCreateRenderPass(app->device, &renderPassInfo, NULL, - &app->renderPass) != VK_SUCCESS) { - fprintf(stderr, "Failed to create render pass!"); - exit(EXIT_FAILURE); - } -} - -void createFramebuffers(Application *app) { - app->swapChainFramebufferCount = app->swapChainImageViewCount; - app->swapChainFramebuffers = - malloc(app->swapChainFramebufferCount * sizeof(VkFramebuffer)); - - for (size_t i = 0; i < app->swapChainImageViewCount; i++) { - VkImageView attachments[] = {app->swapChainImageViews[i]}; - - VkFramebufferCreateInfo framebufferInfo = {0}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = app->renderPass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = app->swapChainExtent.width; - framebufferInfo.height = app->swapChainExtent.height; - framebufferInfo.layers = 1; - - if (vkCreateFramebuffer(app->device, &framebufferInfo, NULL, - &app->swapChainFramebuffers[i]) != VK_SUCCESS) { - fprintf(stderr, "Failed to create framebuffer!"); - exit(EXIT_FAILURE); - } - } -} - -void CreateCommandPool(Application *app) { - QueueFamilyIndices queueFamilyIndices = - findQueueFamilies(app->physicalDevice, &app->surface); - - VkCommandPoolCreateInfo poolInfo = {0}; - poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - - if (vkCreateCommandPool(app->device, &poolInfo, NULL, &app->commandPool) != - VK_SUCCESS) { - fprintf(stderr, "Failed to create command pool!"); - exit(EXIT_FAILURE); - } -} - -void recordCommandBuffer(Application *app, VkCommandBuffer commandBuffer, - uint32_t imageIndex) { - VkCommandBufferBeginInfo beginInfo = {0}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = 0; // Optional - beginInfo.pInheritanceInfo = NULL; // Optional - - if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { - fprintf(stderr, "Failed to begin recording command buffer!"); - exit(EXIT_FAILURE); - } - - VkRenderPassBeginInfo renderPassInfo = {0}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = app->renderPass; - renderPassInfo.framebuffer = app->swapChainFramebuffers[imageIndex]; - - renderPassInfo.renderArea.offset.x = 0; - renderPassInfo.renderArea.offset.y = 0; - renderPassInfo.renderArea.extent = app->swapChainExtent; - - VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; - renderPassInfo.clearValueCount = 1; - renderPassInfo.pClearValues = &clearColor; - - vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, - VK_SUBPASS_CONTENTS_INLINE); - - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - app->graphicsPipeline); - - VkViewport viewport = {0}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = (float)app->swapChainExtent.width; - viewport.height = (float)app->swapChainExtent.height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - - VkRect2D scissor = {0}; - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent = app->swapChainExtent; - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - - vkCmdDraw(commandBuffer, 3, 1, 0, 0); - - vkCmdEndRenderPass(commandBuffer); - - if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { - fprintf(stderr, "Failed to record command buffer!"); - exit(EXIT_FAILURE); - } -} - -void createCommandBuffer(Application *app) { - VkCommandBufferAllocateInfo allocInfo = {0}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = app->commandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = MAX_FRAMES_IN_FLIGHT; - - if (vkAllocateCommandBuffers(app->device, &allocInfo, app->commandBuffers) != - VK_SUCCESS) { - fprintf(stderr, "Failed to allocate command buffers!"); - exit(EXIT_FAILURE); - } -} - -void createSyncObjects(Application *app) { - VkSemaphoreCreateInfo semaphoreInfo = {0}; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VkFenceCreateInfo fenceInfo = {0}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - if (vkCreateSemaphore(app->device, &semaphoreInfo, NULL, - &app->imageAvailableSemaphores[i]) != VK_SUCCESS || - vkCreateSemaphore(app->device, &semaphoreInfo, NULL, - &app->renderFinishedSemaphore[i]) != VK_SUCCESS || - vkCreateFence(app->device, &fenceInfo, NULL, &app->inFlightFences[i]) != - VK_SUCCESS) { - fprintf(stderr, "Failed to create semaphores!"); - exit(EXIT_FAILURE); - } - } -} - -void cleanupSwapChain(Application *app) { - - for (size_t i = 0; i < app->swapChainFramebufferCount; i++) { - vkDestroyFramebuffer(app->device, app->swapChainFramebuffers[i], NULL); - } - - for (size_t i = 0; i < app->swapChainImageViewCount; i++) { - vkDestroyImageView(app->device, app->swapChainImageViews[i], NULL); - } - - free(app->swapChainFramebuffers); - free(app->swapChainImageViews); - free(app->swapChainImages); - - vkDestroySwapchainKHR(app->device, app->swapChain, NULL); -} - -void recreateSwapChain(Application *app) { - int width = 0, height = 0; - glfwGetFramebufferSize(app->window, &width, &height); - while (width == 0 || height == 0) { - glfwGetFramebufferSize(app->window, &width, &height); - glfwWaitEvents(); - } - - vkDeviceWaitIdle(app->device); - cleanupSwapChain(app); - - createSwapChain(app); - createImageViews(app); - createFramebuffers(app); -} - -void initVulkan(Application *app) { - createInstance(app); - setupDebugMessenger(app); - createSurface(app); - pickPhysicalDevice(app); - createLogicalDevice(app); - createSwapChain(app); - createImageViews(app); - createRenderPass(app); - createGraphicsPipeline(app); - createFramebuffers(app); - CreateCommandPool(app); - createCommandBuffer(app); - createSyncObjects(app); -} - -void drawFrame(Application *app) { - vkWaitForFences(app->device, 1, &app->inFlightFences[app->currentFrame], - VK_TRUE, UINT64_MAX); - - uint32_t imageIndex; - VkResult result = - vkAcquireNextImageKHR(app->device, app->swapChain, UINT64_MAX, - app->imageAvailableSemaphores[app->currentFrame], - VK_NULL_HANDLE, &imageIndex); - - if (result == VK_ERROR_OUT_OF_DATE_KHR) { - recreateSwapChain(app); - return; - } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { - fprintf(stderr, "Failed to acquire swap chain image!"); - exit(EXIT_FAILURE); - } - - // Only reset the fence if we are submitting work - vkResetFences(app->device, 1, &app->inFlightFences[app->currentFrame]); - - vkResetCommandBuffer(app->commandBuffers[app->currentFrame], 0); - recordCommandBuffer(app, app->commandBuffers[app->currentFrame], imageIndex); - VkSubmitInfo submitInfo = {0}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - VkSemaphore waitSemaphores[] = { - app->imageAvailableSemaphores[app->currentFrame]}; - VkPipelineStageFlags waitStages[] = { - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &app->commandBuffers[app->currentFrame]; - VkSemaphore signalSemaphores[] = { - app->renderFinishedSemaphore[app->currentFrame]}; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; - - if (vkQueueSubmit(app->graphicsQueue, 1, &submitInfo, - app->inFlightFences[app->currentFrame]) != VK_SUCCESS) { - fprintf(stderr, "failed to submit draw command buffer!"); - exit(EXIT_FAILURE); - } - - VkPresentInfoKHR presentInfo = {0}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = signalSemaphores; - VkSwapchainKHR swapChains[] = {app->swapChain}; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = swapChains; - presentInfo.pImageIndices = &imageIndex; - presentInfo.pResults = NULL; // Optional - - result = vkQueuePresentKHR(app->presentQueue, &presentInfo); - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || - framebufferResized) { - framebufferResized = false; - recreateSwapChain(app); - } else if (result != VK_SUCCESS) { - fprintf(stderr, "failed to present swap chain image!"); - exit(EXIT_FAILURE); - } - - app->currentFrame = (app->currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; -} - -void mainLoop(Application *app) { - while (!glfwWindowShouldClose(app->window)) { - glfwPollEvents(); - drawFrame(app); - } - - vkDeviceWaitIdle(app->device); -} - -void cleanup(Application *app) { - cleanupSwapChain(app); - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(app->device, app->imageAvailableSemaphores[i], NULL); - vkDestroySemaphore(app->device, app->renderFinishedSemaphore[i], NULL); - vkDestroyFence(app->device, app->inFlightFences[i], NULL); - } - vkDestroyCommandPool(app->device, app->commandPool, NULL); - - vkDestroyPipeline(app->device, app->graphicsPipeline, NULL); - vkDestroyPipelineLayout(app->device, app->pipelineLayout, NULL); - - vkDestroyRenderPass(app->device, app->renderPass, NULL); - - vkDestroyDevice(app->device, NULL); - if (enableValidationLayers) { - DestroyDebugUtilsMessengerEXT(app->instance, app->debugMessenger, NULL); - } - vkDestroySurfaceKHR(app->instance, app->surface, NULL); - vkDestroyInstance(app->instance, NULL); - glfwDestroyWindow(app->window); - glfwTerminate(); -} int main(void) { Application app = {0}; diff --git a/src/vulkan_wrapper.c b/src/vulkan_wrapper.c index e69de29..9efbc8e 100644 --- a/src/vulkan_wrapper.c +++ b/src/vulkan_wrapper.c @@ -0,0 +1,1141 @@ +#include "vulkan_wrapper.h" + +#include "dyn_arr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GLFW_INCLUDE_VULKAN +#include + +#ifdef NDEBUG +const bool enableValidationLayers = false; +#else +const bool enableValidationLayers = true; +#endif + +static VKAPI_ATTR VkBool32 VKAPI_CALL +debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, + void *pUserData) { + // TODO: Put all messages into a log file + if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { + fprintf(stderr, "validation layer: %s\n", pCallbackData->pMessage); + } + + return VK_FALSE; +} + +const char *deviceExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; +size_t deviceExtentionCount = + sizeof(deviceExtensions) / sizeof(deviceExtensions[0]); + +const char *validationLayers[] = {"VK_LAYER_KHRONOS_validation"}; +size_t validationLayerCount = + sizeof(validationLayers) / sizeof(validationLayers[0]); + +static bool framebufferResized; // initialised to 0 + + +typedef struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + uint32_t numFormats; + VkSurfaceFormatKHR *formats; + uint32_t numPresentModes; + VkPresentModeKHR *presentModes; +} SwapChainSupportDetails; + +bool checkValidationLayerSupport() { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, NULL); + VkLayerProperties availableLayers[layerCount]; + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers); + + for (int i = 0; i < validationLayerCount; i++) { + bool layerFound = false; + for (int j = 0; j < layerCount; j++) { + if (strcmp(availableLayers[j].layerName, validationLayers[i]) == 0) { + layerFound = true; + break; + } + } + if (!layerFound) { + return false; + } + } + return true; +} + +bool verifyExtensionSupport(uint32_t extensionCount, + VkExtensionProperties *extentions, + uint32_t glfwExtensionCount, + const char **glfwExtensions) { + for (uint32_t i = 0; i < glfwExtensionCount; i++) { + bool layerFound = false; + for (uint32_t j = 0; j < extensionCount; j++) { + if (strcmp(glfwExtensions[i], extentions[j].extensionName) == 0) { + layerFound = true; + break; + } + } + if (!layerFound) { + fprintf(stderr, "Missing %s vulkan extention\n", glfwExtensions[i]); + return false; + } + } + return true; +} + +void populateDebugMessengerCreateInfo( + VkDebugUtilsMessengerCreateInfoEXT *createInfo) { + createInfo->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo->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; + createInfo->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; + createInfo->pfnUserCallback = debugCallback; +} + +void createInstance(Application *app) { + if (enableValidationLayers && !checkValidationLayerSupport()) { + fprintf(stderr, "Validation layers requested, but not available!\n"); + exit(EXIT_FAILURE); + } + + VkApplicationInfo appInfo = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pApplicationName = "Hello Triangle", + .applicationVersion = VK_MAKE_VERSION(1, 0, 0), + .pEngineName = "No Engine", + .engineVersion = VK_MAKE_VERSION(1, 0, 0), + .apiVersion = VK_API_VERSION_1_0, + }; + + uint32_t glfwExtensionCount = 0; + const char **glfwExtensions; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + const char *glfwExtensionsDebug[glfwExtensionCount + 1]; + + VkInstanceCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &appInfo, + .enabledExtensionCount = glfwExtensionCount, + .ppEnabledExtensionNames = glfwExtensions, + .enabledLayerCount = 0, + }; + + uint32_t extensionCount = 0; + vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL); + VkExtensionProperties extensions[extensionCount]; + vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions); + + VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo = {0}; + if (enableValidationLayers) { + populateDebugMessengerCreateInfo(&debugCreateInfo); + createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo; + + createInfo.enabledLayerCount = validationLayerCount; + createInfo.ppEnabledLayerNames = validationLayers; + + for (size_t i = 0; i < glfwExtensionCount; i++) { + glfwExtensionsDebug[i] = glfwExtensions[i]; + } + glfwExtensionsDebug[glfwExtensionCount] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + createInfo.enabledExtensionCount = glfwExtensionCount + 1; + createInfo.ppEnabledExtensionNames = glfwExtensionsDebug; + if (!verifyExtensionSupport(extensionCount, extensions, + glfwExtensionCount + 1, glfwExtensionsDebug)) { + fprintf( + stderr, + "Failed to find all required vulkan extentions for glfw and debug\n"); + exit(EXIT_FAILURE); + } + } else { + if (!verifyExtensionSupport(extensionCount, extensions, glfwExtensionCount, + glfwExtensions)) { + fprintf(stderr, + "Failed to find all required vulkan extentions for glfw\n"); + exit(EXIT_FAILURE); + } + } + + // This currently leaks memory on laptop :( + VkResult result = vkCreateInstance(&createInfo, NULL, &app->instance); + if (result != VK_SUCCESS) { + fprintf(stderr, "Failed to create vulkan instance\n"); + exit(EXIT_FAILURE); + } +} + +static void framebufferResizeCallback(GLFWwindow *window, int width, + int height) { + framebufferResized = true; +} + +void initWindow(Application *app) { + glfwInit(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + app->window = glfwCreateWindow(800, 600, "Vulkan", NULL, NULL); + + glfwSetFramebufferSizeCallback(app->window, framebufferResizeCallback); +} + +VkResult CreateDebugUtilsMessengerEXT( + VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkDebugUtilsMessengerEXT *pDebugMessenger) { + PFN_vkCreateDebugUtilsMessengerEXT func = + (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != NULL) { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void setupDebugMessenger(Application *app) { + if (!enableValidationLayers) { + return; + } + if (!checkValidationLayerSupport()) { + fprintf(stderr, "Validation layers requested, but not available!\n"); + exit(EXIT_FAILURE); + } + VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; + populateDebugMessengerCreateInfo(&createInfo); + + if (CreateDebugUtilsMessengerEXT(app->instance, &createInfo, NULL, + &app->debugMessenger) != VK_SUCCESS) { + fprintf(stderr, "failed to set up debug messenger!\n"); + } +} + +void DestroyDebugUtilsMessengerEXT(VkInstance instance, + VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks *pAllocator) { + PFN_vkDestroyDebugUtilsMessengerEXT func = + (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != NULL) { + func(instance, debugMessenger, pAllocator); + } +} + +struct QueueFamilyIndices_s { + bool graphicsFamilyExists; + uint32_t graphicsFamily; + bool presentFamilyExists; + uint32_t presentFamily; +} QueueFamilyIndices_default = { + .graphicsFamilyExists = false, + .graphicsFamily = 0, + .presentFamilyExists = false, + .presentFamily = 0, +}; +typedef struct QueueFamilyIndices_s QueueFamilyIndices; + +QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device, + VkSurfaceKHR *surface) { + QueueFamilyIndices indices = QueueFamilyIndices_default; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, NULL); + VkQueueFamilyProperties queueFamilies[queueFamilyCount]; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, + queueFamilies); + for (uint32_t i = 0; i < queueFamilyCount; i++) { + // Graphics support + if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + indices.graphicsFamilyExists = true; + } + // Surface support + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, *surface, &presentSupport); + if (presentSupport) { + indices.presentFamily = i; + indices.presentFamilyExists = true; + } + + // Early break if we've found everything + if (indices.presentFamilyExists && indices.graphicsFamilyExists) { + break; + } + } + + return indices; +} + +bool checkDeviceExtensionSupport(VkPhysicalDevice device) { + uint32_t availableExtensionCount; + vkEnumerateDeviceExtensionProperties(device, NULL, &availableExtensionCount, + NULL); + VkExtensionProperties availableExtentions[availableExtensionCount]; + vkEnumerateDeviceExtensionProperties(device, NULL, &availableExtensionCount, + availableExtentions); + + for (uint i = 0; i < deviceExtentionCount; i++) { + bool extentionFound = false; + for (uint j = 0; j < availableExtensionCount; j++) { + if (strcmp(availableExtentions[j].extensionName, deviceExtensions[i]) == + 0) { + extentionFound = true; + break; + } + } + if (!extentionFound) { + return false; + } + } + return true; +} + +// TODO: find a better way of managing memory +SwapChainSupportDetails querySwapchainSupport(VkPhysicalDevice device, + VkSurfaceKHR *surface) { + SwapChainSupportDetails details = {0}; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, *surface, + &details.capabilities); + + vkGetPhysicalDeviceSurfaceFormatsKHR(device, *surface, &details.numFormats, + NULL); + if (details.numFormats > 0) { + details.formats = malloc(details.numFormats * sizeof(VkSurfaceFormatKHR)); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, *surface, &details.numFormats, + details.formats); + } + + vkGetPhysicalDeviceSurfacePresentModesKHR(device, *surface, + &details.numPresentModes, NULL); + if (details.numPresentModes > 0) { + details.presentModes = + malloc(details.numPresentModes * sizeof(VkPresentModeKHR)); + vkGetPhysicalDeviceSurfacePresentModesKHR( + device, *surface, &details.numPresentModes, details.presentModes); + } + + return details; +} + +// Prefer one format but otherwise go for +VkSurfaceFormatKHR chooseSwapSurfaceFormat(VkSurfaceFormatKHR *availableFormats, + size_t availableFormatCount) { + for (uint i = 0; i < availableFormatCount; i++) { + if (availableFormats[i].format == VK_FORMAT_B8G8R8_SRGB && + availableFormats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return availableFormats[i]; + } + } + return availableFormats[0]; +} + +// Prefer mailbox otherwise go for the garenteed available present mode +VkPresentModeKHR chooseSwapPresentMode(VkPresentModeKHR *availablePresentModes, + size_t availablePresentModesCount) { + for (uint i = 0; i < availablePresentModesCount; i++) { + if (availablePresentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { + return availablePresentModes[i]; + } + } + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D chooseSwapExtent(GLFWwindow *window, + const VkSurfaceCapabilitiesKHR *capabilities) { + if (capabilities->currentExtent.width != UINT_MAX) { + return capabilities->currentExtent; + } + + int width, height; + glfwGetFramebufferSize(window, &width, &height); + VkExtent2D actualExtent = {(uint32_t)width, (uint32_t)height}; + + // check width and height are within bounds + actualExtent.width = + fmin(capabilities->maxImageExtent.width, actualExtent.width); + actualExtent.width = + fmax(capabilities->minImageExtent.width, actualExtent.width); + actualExtent.height = + fmin(capabilities->maxImageExtent.height, actualExtent.height); + actualExtent.height = + fmax(capabilities->minImageExtent.height, actualExtent.height); + + return actualExtent; +} + +bool isDeviceSuitable(VkPhysicalDevice device, VkSurfaceKHR *surface) { + QueueFamilyIndices indices = findQueueFamilies(device, surface); + bool completeIndeces = + indices.graphicsFamilyExists && indices.presentFamilyExists; + + bool extensionsSupported = checkDeviceExtensionSupport(device); + + bool swapChainAdequate = false; + if (extensionsSupported) { + SwapChainSupportDetails swapChainDetails = + querySwapchainSupport(device, surface); + + if (swapChainDetails.numFormats > 0 && + swapChainDetails.numPresentModes > 0) { + swapChainAdequate = true; + } + + free(swapChainDetails.presentModes); + free(swapChainDetails.formats); + } + + return completeIndeces && extensionsSupported && swapChainAdequate; +} + +void pickPhysicalDevice(Application *app) { + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + + uint32_t physicalDeviceCount; + vkEnumeratePhysicalDevices(app->instance, &physicalDeviceCount, NULL); + if (physicalDeviceCount == 0) { + fprintf(stderr, "Failed to find GPU with vulkan support!\n"); + fprintf(stderr, "Failed to find GPU with vulkan support!\n"); + exit(EXIT_FAILURE); + } + VkPhysicalDevice physicalDevices[physicalDeviceCount]; + vkEnumeratePhysicalDevices(app->instance, &physicalDeviceCount, + physicalDevices); + + // TODO: pick device off of more than if it's just suitable + for (int i = 0; i < physicalDeviceCount; i++) { + if (isDeviceSuitable(physicalDevices[i], &app->surface)) { + physicalDevice = physicalDevices[i]; + break; + } + } + + if (physicalDevice == VK_NULL_HANDLE) { + fprintf(stderr, "Failed to find suitable GPU\n"); + exit(EXIT_FAILURE); + } + + app->physicalDevice = physicalDevice; +} + +void createLogicalDevice(Application *app) { + QueueFamilyIndices indices = + findQueueFamilies(app->physicalDevice, &app->surface); + + uint32_t queueFamilies[] = {indices.graphicsFamily, indices.presentFamily}; + size_t numQueues = sizeof(queueFamilies) / sizeof(uint32_t); + + // Make sure queue families don't have duplicates + // TODO: eventually move over to a set rather than a dynamic array + uint32_t *uniqueQueueFamilies = dyna_init(uint32_t); + for (uint i = 0; i < numQueues; i++) { + bool isUnique = true; + for (uint j = 0; j < dyna_length(uniqueQueueFamilies); j++) { + if (uniqueQueueFamilies[j] == queueFamilies[i]) { + isUnique = false; + break; + } + } + if (isUnique) { + dyna_append(uniqueQueueFamilies, queueFamilies[i]); + } + } + VkDeviceQueueCreateInfo queueCreateInfos[dyna_length(uniqueQueueFamilies)]; + + for (uint i = 0; i < dyna_length(uniqueQueueFamilies); i++) { + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = uniqueQueueFamilies[i]; + queueCreateInfo.queueCount = 1; + float queuePriority = 1.0f; + queueCreateInfo.pQueuePriorities = &queuePriority; + + queueCreateInfos[i] = queueCreateInfo; + } + + // Specify device features + VkPhysicalDeviceFeatures deviceFeatures = {VK_FALSE}; + + // Specify logical device + VkDeviceCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.pQueueCreateInfos = queueCreateInfos; + createInfo.queueCreateInfoCount = dyna_length(uniqueQueueFamilies); + createInfo.pEnabledFeatures = &deviceFeatures; + createInfo.enabledExtensionCount = deviceExtentionCount; + createInfo.ppEnabledExtensionNames = deviceExtensions; + + dyna_deinit(uniqueQueueFamilies); + + // Old Vulkan specify layers + createInfo.enabledLayerCount = 0; + createInfo.ppEnabledLayerNames = 0; + if (enableValidationLayers) { + createInfo.enabledLayerCount = validationLayerCount; + createInfo.ppEnabledLayerNames = validationLayers; + } + + if (vkCreateDevice(app->physicalDevice, &createInfo, NULL, &app->device) != + VK_SUCCESS) { + fprintf(stderr, "Failed to create logical device.\n"); + exit(EXIT_FAILURE); + } + + // Set graphics queue from logical device + vkGetDeviceQueue(app->device, indices.graphicsFamily, 0, &app->graphicsQueue); + vkGetDeviceQueue(app->device, indices.presentFamily, 0, &app->presentQueue); +} + +void createSurface(Application *app) { + if (glfwCreateWindowSurface(app->instance, app->window, NULL, + &app->surface) != VK_SUCCESS) { + fprintf(stderr, "Error creating vulkan surface in glfw window."); + exit(EXIT_FAILURE); + } +} +void createSwapChain(Application *app) { + SwapChainSupportDetails swapChainSupport = + querySwapchainSupport(app->physicalDevice, &app->surface); + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat( + swapChainSupport.formats, swapChainSupport.numFormats); + VkPresentModeKHR presentMode = chooseSwapPresentMode( + swapChainSupport.presentModes, swapChainSupport.numPresentModes); + VkExtent2D extent = + chooseSwapExtent(app->window, &swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && + imageCount > swapChainSupport.capabilities.maxImageCount) { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = app->surface; + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = + findQueueFamilies(app->physicalDevice, &app->surface); + uint32_t queueFamilyIndices[] = {indices.graphicsFamily, + indices.presentFamily}; + + if (indices.graphicsFamily != indices.presentFamily) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = NULL; + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(app->device, &createInfo, NULL, &app->swapChain) != + VK_SUCCESS) { + fprintf(stderr, "Failed to create swapchain!"); + exit(EXIT_FAILURE); + } + + vkGetSwapchainImagesKHR(app->device, app->swapChain, + &app->swapChainImageCount, NULL); + app->swapChainImages = malloc(app->swapChainImageCount * sizeof(VkImage)); + vkGetSwapchainImagesKHR(app->device, app->swapChain, + &app->swapChainImageCount, app->swapChainImages); + + app->swapChainImageFormat = surfaceFormat.format; + app->swapChainExtent = extent; + + free(swapChainSupport.presentModes); + free(swapChainSupport.formats); +} + +void createImageViews(Application *app) { + app->swapChainImageViewCount = app->swapChainImageCount; + app->swapChainImageViews = + malloc(app->swapChainImageViewCount * sizeof(VkImageView)); + + for (size_t i = 0; i < app->swapChainImageViewCount; i++) { + VkImageViewCreateInfo createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = app->swapChainImages[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = app->swapChainImageFormat; + + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(app->device, &createInfo, NULL, + &app->swapChainImageViews[i]) != VK_SUCCESS) { + fprintf(stderr, "Failed to create image views!\n"); + exit(EXIT_FAILURE); + } + } +} + +// Buffer should be freed +typedef struct { + char *buffer; + size_t bufferSize; +} ShaderBuffer; + +ShaderBuffer loadShaderIntoBuffer(const char *fileLocation) { + ShaderBuffer buf = {.buffer = NULL, .bufferSize = 0}; + FILE *file = fopen(fileLocation, "r"); + + if (file == NULL) { + fprintf(stderr, "Could not open file \"%s\"!\n", fileLocation); + exit(EXIT_FAILURE); + } + + if (fseek(file, 0L, SEEK_END) != 0) { + fprintf(stderr, "Could not seek to the end of file \"%s\"!\n", + fileLocation); + exit(EXIT_FAILURE); + } + buf.bufferSize = ftell(file); + + if (fseek(file, 0L, SEEK_SET) != 0) { + fprintf(stderr, "Could not seek to the start of file \"%s\"!\n", + fileLocation); + exit(EXIT_FAILURE); + } + buf.buffer = + malloc(sizeof(char) * (buf.bufferSize)); // Not '\0' terminting ourselves + + size_t tempSize = fread(buf.buffer, sizeof(char), buf.bufferSize, file); + if (ferror(file) != 0) { + fprintf(stderr, "Error reading file into buffer \"%s\"!\n", fileLocation); + exit(EXIT_FAILURE); + } + fclose(file); + + return buf; +} + +VkShaderModule createShaderModule(VkDevice device, ShaderBuffer code) { + VkShaderModuleCreateInfo createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.bufferSize; + createInfo.pCode = (uint32_t *)code.buffer; + + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, NULL, &shaderModule) != + VK_SUCCESS) { + fprintf(stderr, "Failed to create shaderModule from Shader Code!"); + exit(EXIT_FAILURE); + } + + return shaderModule; +} + +void createGraphicsPipeline(Application *app) { + ShaderBuffer vertShaderCode = loadShaderIntoBuffer("shaders/shader.vert.spv"); + ShaderBuffer fragShaderCode = loadShaderIntoBuffer("shaders/shader.frag.spv"); + + VkShaderModule vertShaderModule = + createShaderModule(app->device, vertShaderCode); + VkShaderModule fragShaderModule = + createShaderModule(app->device, fragShaderCode); + + free(vertShaderCode.buffer); + free(fragShaderCode.buffer); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo = {0}; + vertShaderStageInfo.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + vertShaderStageInfo.pSpecializationInfo = NULL; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo = {0}; + fragShaderStageInfo.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + vertShaderStageInfo.pSpecializationInfo = NULL; + + VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, + fragShaderStageInfo}; + + size_t dynamicStatesCount = 2; + VkDynamicState dynamicStates[] = {VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamicState = {0}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.dynamicStateCount = dynamicStatesCount; + dynamicState.pDynamicStates = dynamicStates; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {0}; + vertexInputInfo.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.pVertexBindingDescriptions = NULL; // Optional + vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.pVertexAttributeDescriptions = NULL; // Optional + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {0}; + inputAssembly.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {0}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)app->swapChainExtent.width; + viewport.height = (float)app->swapChainExtent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkPipelineViewportStateCreateInfo viewportState = {0}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo rasterizer = {0}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; // Optional + rasterizer.depthBiasClamp = 0.0f; // Optional + rasterizer.depthBiasSlopeFactor = 0.0f; // Optional + + // disable multisampling for now + VkPipelineMultisampleStateCreateInfo multisampling = {0}; + multisampling.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; // Optional + multisampling.pSampleMask = NULL; // Optional + multisampling.alphaToCoverageEnable = VK_FALSE; // Optional + multisampling.alphaToOneEnable = VK_FALSE; // Optional + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {0}; + colorBlendAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional + + VkPipelineColorBlendStateCreateInfo colorBlending = {0}; + colorBlending.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {0}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 0; // Optional + pipelineLayoutInfo.pSetLayouts = NULL; // Optional + pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional + pipelineLayoutInfo.pPushConstantRanges = NULL; // Optional + + if (vkCreatePipelineLayout(app->device, &pipelineLayoutInfo, NULL, + &app->pipelineLayout) != VK_SUCCESS) { + fprintf(stderr, "Failed to create pipeline layout!"); + exit(EXIT_FAILURE); + } + + VkGraphicsPipelineCreateInfo pipelineInfo = {0}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + pipelineInfo.layout = app->pipelineLayout; + pipelineInfo.renderPass = app->renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + + if (vkCreateGraphicsPipelines(app->device, VK_NULL_HANDLE, 1, &pipelineInfo, + NULL, &app->graphicsPipeline) != VK_SUCCESS) { + fprintf(stderr, "Failed to create graphics pipeline!"); + exit(EXIT_FAILURE); + } + + vkDestroyShaderModule(app->device, fragShaderModule, NULL); + vkDestroyShaderModule(app->device, vertShaderModule, NULL); +} + +void createRenderPass(Application *app) { + VkAttachmentDescription colorAttachment = {0}; + colorAttachment.format = app->swapChainImageFormat; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef = {0}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {0}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkSubpassDependency dependency = {0}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo renderPassInfo = {0}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(app->device, &renderPassInfo, NULL, + &app->renderPass) != VK_SUCCESS) { + fprintf(stderr, "Failed to create render pass!"); + exit(EXIT_FAILURE); + } +} + +void createFramebuffers(Application *app) { + app->swapChainFramebufferCount = app->swapChainImageViewCount; + app->swapChainFramebuffers = + malloc(app->swapChainFramebufferCount * sizeof(VkFramebuffer)); + + for (size_t i = 0; i < app->swapChainImageViewCount; i++) { + VkImageView attachments[] = {app->swapChainImageViews[i]}; + + VkFramebufferCreateInfo framebufferInfo = {0}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = app->renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = app->swapChainExtent.width; + framebufferInfo.height = app->swapChainExtent.height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(app->device, &framebufferInfo, NULL, + &app->swapChainFramebuffers[i]) != VK_SUCCESS) { + fprintf(stderr, "Failed to create framebuffer!"); + exit(EXIT_FAILURE); + } + } +} + +void CreateCommandPool(Application *app) { + QueueFamilyIndices queueFamilyIndices = + findQueueFamilies(app->physicalDevice, &app->surface); + + VkCommandPoolCreateInfo poolInfo = {0}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; + + if (vkCreateCommandPool(app->device, &poolInfo, NULL, &app->commandPool) != + VK_SUCCESS) { + fprintf(stderr, "Failed to create command pool!"); + exit(EXIT_FAILURE); + } +} + +void recordCommandBuffer(Application *app, VkCommandBuffer commandBuffer, + uint32_t imageIndex) { + VkCommandBufferBeginInfo beginInfo = {0}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; // Optional + beginInfo.pInheritanceInfo = NULL; // Optional + + if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { + fprintf(stderr, "Failed to begin recording command buffer!"); + exit(EXIT_FAILURE); + } + + VkRenderPassBeginInfo renderPassInfo = {0}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = app->renderPass; + renderPassInfo.framebuffer = app->swapChainFramebuffers[imageIndex]; + + renderPassInfo.renderArea.offset.x = 0; + renderPassInfo.renderArea.offset.y = 0; + renderPassInfo.renderArea.extent = app->swapChainExtent; + + VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, + VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + app->graphicsPipeline); + + VkViewport viewport = {0}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)app->swapChainExtent.width; + viewport.height = (float)app->swapChainExtent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor = {0}; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent = app->swapChainExtent; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffer); + + if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { + fprintf(stderr, "Failed to record command buffer!"); + exit(EXIT_FAILURE); + } +} + +void createCommandBuffer(Application *app) { + VkCommandBufferAllocateInfo allocInfo = {0}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = app->commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = MAX_FRAMES_IN_FLIGHT; + + if (vkAllocateCommandBuffers(app->device, &allocInfo, app->commandBuffers) != + VK_SUCCESS) { + fprintf(stderr, "Failed to allocate command buffers!"); + exit(EXIT_FAILURE); + } +} + +void createSyncObjects(Application *app) { + VkSemaphoreCreateInfo semaphoreInfo = {0}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo = {0}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + if (vkCreateSemaphore(app->device, &semaphoreInfo, NULL, + &app->imageAvailableSemaphores[i]) != VK_SUCCESS || + vkCreateSemaphore(app->device, &semaphoreInfo, NULL, + &app->renderFinishedSemaphore[i]) != VK_SUCCESS || + vkCreateFence(app->device, &fenceInfo, NULL, &app->inFlightFences[i]) != + VK_SUCCESS) { + fprintf(stderr, "Failed to create semaphores!"); + exit(EXIT_FAILURE); + } + } +} + +void cleanupSwapChain(Application *app) { + + for (size_t i = 0; i < app->swapChainFramebufferCount; i++) { + vkDestroyFramebuffer(app->device, app->swapChainFramebuffers[i], NULL); + } + + for (size_t i = 0; i < app->swapChainImageViewCount; i++) { + vkDestroyImageView(app->device, app->swapChainImageViews[i], NULL); + } + + free(app->swapChainFramebuffers); + free(app->swapChainImageViews); + free(app->swapChainImages); + + vkDestroySwapchainKHR(app->device, app->swapChain, NULL); +} + +void recreateSwapChain(Application *app) { + int width = 0, height = 0; + glfwGetFramebufferSize(app->window, &width, &height); + while (width == 0 || height == 0) { + glfwGetFramebufferSize(app->window, &width, &height); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(app->device); + cleanupSwapChain(app); + + createSwapChain(app); + createImageViews(app); + createFramebuffers(app); +} + +void initVulkan(Application *app) { + createInstance(app); + setupDebugMessenger(app); + createSurface(app); + pickPhysicalDevice(app); + createLogicalDevice(app); + createSwapChain(app); + createImageViews(app); + createRenderPass(app); + createGraphicsPipeline(app); + createFramebuffers(app); + CreateCommandPool(app); + createCommandBuffer(app); + createSyncObjects(app); +} + +void drawFrame(Application *app) { + vkWaitForFences(app->device, 1, &app->inFlightFences[app->currentFrame], + VK_TRUE, UINT64_MAX); + + uint32_t imageIndex; + VkResult result = + vkAcquireNextImageKHR(app->device, app->swapChain, UINT64_MAX, + app->imageAvailableSemaphores[app->currentFrame], + VK_NULL_HANDLE, &imageIndex); + + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(app); + return; + } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + fprintf(stderr, "Failed to acquire swap chain image!"); + exit(EXIT_FAILURE); + } + + // Only reset the fence if we are submitting work + vkResetFences(app->device, 1, &app->inFlightFences[app->currentFrame]); + + vkResetCommandBuffer(app->commandBuffers[app->currentFrame], 0); + recordCommandBuffer(app, app->commandBuffers[app->currentFrame], imageIndex); + VkSubmitInfo submitInfo = {0}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = { + app->imageAvailableSemaphores[app->currentFrame]}; + VkPipelineStageFlags waitStages[] = { + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &app->commandBuffers[app->currentFrame]; + VkSemaphore signalSemaphores[] = { + app->renderFinishedSemaphore[app->currentFrame]}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + if (vkQueueSubmit(app->graphicsQueue, 1, &submitInfo, + app->inFlightFences[app->currentFrame]) != VK_SUCCESS) { + fprintf(stderr, "failed to submit draw command buffer!"); + exit(EXIT_FAILURE); + } + + VkPresentInfoKHR presentInfo = {0}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + VkSwapchainKHR swapChains[] = {app->swapChain}; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = NULL; // Optional + + result = vkQueuePresentKHR(app->presentQueue, &presentInfo); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || + framebufferResized) { + framebufferResized = false; + recreateSwapChain(app); + } else if (result != VK_SUCCESS) { + fprintf(stderr, "failed to present swap chain image!"); + exit(EXIT_FAILURE); + } + + app->currentFrame = (app->currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; +} + +void mainLoop(Application *app) { + while (!glfwWindowShouldClose(app->window)) { + glfwPollEvents(); + drawFrame(app); + } + + vkDeviceWaitIdle(app->device); +} + +void cleanup(Application *app) { + cleanupSwapChain(app); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(app->device, app->imageAvailableSemaphores[i], NULL); + vkDestroySemaphore(app->device, app->renderFinishedSemaphore[i], NULL); + vkDestroyFence(app->device, app->inFlightFences[i], NULL); + } + vkDestroyCommandPool(app->device, app->commandPool, NULL); + + vkDestroyPipeline(app->device, app->graphicsPipeline, NULL); + vkDestroyPipelineLayout(app->device, app->pipelineLayout, NULL); + + vkDestroyRenderPass(app->device, app->renderPass, NULL); + + vkDestroyDevice(app->device, NULL); + if (enableValidationLayers) { + DestroyDebugUtilsMessengerEXT(app->instance, app->debugMessenger, NULL); + } + vkDestroySurfaceKHR(app->instance, app->surface, NULL); + vkDestroyInstance(app->instance, NULL); + glfwDestroyWindow(app->window); + glfwTerminate(); +} diff --git a/src/vulkan_wrapper.h b/src/vulkan_wrapper.h index 6f70f09..047f99b 100644 --- a/src/vulkan_wrapper.h +++ b/src/vulkan_wrapper.h @@ -1 +1,42 @@ #pragma once + +#include + +#define GLFW_INCLUDE_VULKAN +#include + +#define MAX_FRAMES_IN_FLIGHT 2 + +typedef struct { + GLFWwindow *window; + VkInstance instance; + VkDebugUtilsMessengerEXT debugMessenger; + VkSurfaceKHR surface; + VkPhysicalDevice physicalDevice; + VkDevice device; + VkQueue graphicsQueue; + VkQueue presentQueue; + VkSwapchainKHR swapChain; + VkImage *swapChainImages; + uint32_t swapChainImageCount; + VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; + VkImageView *swapChainImageViews; + uint32_t swapChainImageViewCount; + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + VkFramebuffer *swapChainFramebuffers; + uint32_t swapChainFramebufferCount; + VkCommandPool commandPool; + VkCommandBuffer commandBuffers[MAX_FRAMES_IN_FLIGHT]; + VkSemaphore imageAvailableSemaphores[MAX_FRAMES_IN_FLIGHT]; + VkSemaphore renderFinishedSemaphore[MAX_FRAMES_IN_FLIGHT]; + VkFence inFlightFences[MAX_FRAMES_IN_FLIGHT]; + uint32_t currentFrame; // initialised to 0 +} Application; + +void initWindow(Application *app); +void initVulkan(Application *app); +void mainLoop(Application *app); +void cleanup(Application *app);