summaryrefslogtreecommitdiffstats
path: root/src/game.c
diff options
context:
space:
mode:
authorgramanas <anastasis.gramm2@gmail.com>2024-05-19 04:06:41 +0300
committergramanas <anastasis.gramm2@gmail.com>2024-05-19 04:06:41 +0300
commit9ead62940aecb7793b3d82c9c8eb73bc9eb4f60f (patch)
treee6f614b0e2c5e05bed95705778a0d5d5dedbe446 /src/game.c
downloadcgame-9ead62940aecb7793b3d82c9c8eb73bc9eb4f60f.tar.gz
cgame-9ead62940aecb7793b3d82c9c8eb73bc9eb4f60f.tar.bz2
cgame-9ead62940aecb7793b3d82c9c8eb73bc9eb4f60f.zip
aait
Diffstat (limited to 'src/game.c')
-rw-r--r--src/game.c1601
1 files changed, 1601 insertions, 0 deletions
diff --git a/src/game.c b/src/game.c
new file mode 100644
index 0000000..d6ec303
--- /dev/null
+++ b/src/game.c
@@ -0,0 +1,1601 @@
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <shaderc/shaderc.h>
+
+#define SDL_MAIN_HANDLED
+#define VK_USE_PLATFORM_XCB_KHR
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+#include <SDL2/SDL_vulkan.h>
+
+#include <vulkan/vulkan.h>
+#include <vulkan/vk_enum_string_helper.h> // for string_VkResult
+
+#define VMA_STATIC_VULKAN_FUNCTIONS 0
+#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
+#include "vk_mem_alloc.h"
+
+//#include "cplusplus.h"
+#include "vkutil.h"
+#include "state.h"
+
+// embedded clgm library
+#define CGLM_FORCE_DEPTH_ZERO_TO_ONE
+#include "../lib/cglm/include/cglm/cglm.h"
+
+uint32_t currentFrame = 0;
+state_t s;
+
+const uint32_t validationLayerCount = 1;
+const char *const validationLayers[] = {
+ "VK_LAYER_KHRONOS_validation"
+};
+
+const uint32_t deviceExtensionCount = 1;
+const char *const deviceExtensions[] = {
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+};
+
+#ifdef VKDEBUG
+ const bool enableValidationLayers = true;
+#else
+ const bool enableValidationLayers = false;
+#endif
+
+typedef struct {
+ float x;
+ float y;
+} V2;
+
+typedef struct {
+ float x;
+ float y;
+ float z;
+} V3;
+
+typedef struct {
+ V2 pos;
+ V3 color;
+} Vertex;
+
+/* Vertex vertices[] = { */
+/* (Vertex) { (V2) {-0.2f, -0.5f}, (V3) {0.0f, 1.0f, 0.0f}}, */
+/* (Vertex) { (V2) {0.5f, 0.3f}, (V3) {0.0f, 0.0f, 1.0f}}, */
+/* (Vertex) { (V2) {-0.5f, 0.7f}, (V3) {1.0f, 0.0f, 0.0f}}, */
+
+/* (Vertex) { (V2) {0.2f, -0.5f}, (V3) {0.0f, 0.0f, 1.0f}}, */
+/* (Vertex) { (V2) {0.5f, 0.7f}, (V3) {1.0f, 0.0f, 0.0f}}, */
+/* (Vertex) { (V2) {-0.5f, 0.3f}, (V3) {0.0f, 1.0f, 0.0f}}, */
+
+/* (Vertex) { (V2) {0.0f, -0.5f}, (V3) {1.0f, 0.0f, 0.0f}}, */
+/* (Vertex) { (V2) {0.5f, 0.5f}, (V3) {0.0f, 1.0f, 0.0f}}, */
+/* (Vertex) { (V2) {-0.5f, 0.5f}, (V3) {0.0f, 0.0f, 1.0f}}, */
+/* }; */
+/* const int VERTICES_SIZE = VK_ARRAY_LEN(vertices); */
+
+/* const uint16_t indices[] = { */
+/* 0, 1, 2, 3, 4, 5, 6, 7, 8, */
+/* }; */
+/* const int INDICES_SIZE = VK_ARRAY_LEN(indices); */
+
+Vertex vertices[] = {
+ (Vertex) { (V2) {-0.5f, -0.5f}, (V3) {1.0f, 0.0f, 0.0f}},
+ (Vertex) { (V2) {0.5f, -0.5f}, (V3) {0.0f, 1.0f, 0.0f}},
+ (Vertex) { (V2) {0.5f, 0.5f}, (V3) {0.0f, 0.0f, 1.0f}},
+ (Vertex) { (V2) {-0.5f, 0.5f}, (V3) {1.0f, 1.0f, 1.0f}},
+};
+const int VERTICES_SIZE = VK_ARRAY_LEN(vertices);
+
+const uint16_t indices[] = {
+ 0, 1, 2, 2, 3, 0
+};
+const int INDICES_SIZE = VK_ARRAY_LEN(indices);
+
+bool
+init()
+{
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ vk_log(VK_INFO, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
+ return false;
+ }
+
+ s.sdl_window = SDL_CreateWindow("Vulkanizater",
+ SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ s.window_w,
+ s.window_h,
+ SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_VULKAN);
+ if (s.sdl_window == NULL) {
+ vk_log(VK_INFO, "Window could not be created! SDL_Error: %s\n", SDL_GetError());
+ return false;
+ }
+
+ return true;
+}
+
+void
+closeSDL()
+{
+ SDL_DestroyWindow(s.sdl_window);
+ s.sdl_window = NULL;
+ SDL_Quit();
+}
+
+bool
+checkValidationLayerSupport()
+{
+ uint32_t layerCount;
+ vkEnumerateInstanceLayerProperties(&layerCount, NULL);
+
+ VkLayerProperties availableLayers[layerCount];
+ vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
+
+ for (uint32_t i = 0; i < validationLayerCount; i++) {
+ bool layerFound = false;
+
+ for (uint32_t j = 0; j < layerCount; j++) {
+ if (strcmp(validationLayers[i], availableLayers[j].layerName) == 0) {
+ layerFound = true;
+ break;
+ }
+ }
+
+ if (!layerFound) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static VKAPI_ATTR VkBool32 VKAPI_CALL
+debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+ VkDebugUtilsMessageTypeFlagsEXT messageType,
+ const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
+ void* pUserData)
+{
+ vk_log(VK_ERROR, "validation layer: %s\n", pCallbackData->pMessage);
+ return VK_FALSE;
+}
+
+void
+vulkan_setup_debug_messenger()
+{
+ if (!enableValidationLayers) return;
+ VkDebugUtilsMessengerCreateInfoEXT createInfo = {0};
+ 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;
+ createInfo.pUserData = NULL; // Optional
+
+
+ // TODO: func pointers returned are NULL
+ s.pfnCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(s.vk_instance, "vkCreateDebugUtilsMessengerEXT");
+ s.pfnDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(s.vk_instance, "vkDestroyDebugUtilsMessengerEXT");
+
+ VkResult result = s.pfnCreateDebugUtilsMessengerEXT(s.vk_instance, &createInfo, NULL, &s.vk_debug_messenger);
+ if (result != VK_SUCCESS) {
+ vk_log(VK_WARN, "failed to set up debug messenger! %s\n", string_VkResult(result));
+ } else {
+ vk_log(VK_INFO, "Debug messanger created!\n");
+ }
+}
+
+void
+vulkan_create_instance()
+{
+ if (enableValidationLayers && !checkValidationLayerSupport()) {
+ vk_log(VK_ERROR, "validation layers requested, but not available!\n");
+ }
+
+ uint32_t instanceVersion;
+ VkResult result = vkEnumerateInstanceVersion(&instanceVersion);
+ if (result == VK_SUCCESS) {
+ if (instanceVersion < VK_MAKE_API_VERSION(0, 1, 3, 0)) {
+ vk_log(VK_ERROR, "Vulkan version 1.3 or greater required!\n");
+ exit(1);
+ }
+ vk_log(VK_INFO, "Vulkan version found (%d) %d.%d.%d\n",
+ VK_API_VERSION_VARIANT(instanceVersion),
+ VK_API_VERSION_MAJOR(instanceVersion),
+ VK_API_VERSION_MINOR(instanceVersion),
+ VK_API_VERSION_PATCH(instanceVersion));
+ } else {
+ vk_log(VK_ERROR, "Failed to retrieve vulkan version, is vulkan supported in this system? %s\n", string_VkResult(result));
+ exit(1);
+ }
+
+ // Load Vulkan and create instance
+ VkApplicationInfo appInfo = {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pApplicationName = "Vulkan Application",
+ .applicationVersion = VK_MAKE_API_VERSION(0, 1, 3, 0),
+ .pEngineName = NULL,
+ .engineVersion = VK_MAKE_API_VERSION(0, 1, 3, 0),
+ .apiVersion = VK_MAKE_API_VERSION(0, 1, 3, 0),
+ };
+
+ uint32_t sdlExtensionCount = 0;
+
+ if (SDL_Vulkan_GetInstanceExtensions(s.sdl_window, &sdlExtensionCount, NULL) == SDL_FALSE) {
+ vk_log(VK_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", SDL_GetError());
+ }
+
+ // make space for debug extenetion
+ if (enableValidationLayers) {
+ sdlExtensionCount++;
+ }
+
+ const char* sdlExtensions[sdlExtensionCount];
+
+ if (SDL_Vulkan_GetInstanceExtensions(s.sdl_window, &sdlExtensionCount, sdlExtensions) == SDL_FALSE) {
+ vk_log(VK_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", SDL_GetError());
+ }
+
+ // add debug extenetion
+ if (enableValidationLayers) {
+ sdlExtensions[sdlExtensionCount] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
+ }
+
+ vk_log(VK_INFO, "The sdl extensions:\n");
+ for (uint32_t i = 0; i < sdlExtensionCount; i++) {
+ vk_log(VK_INFO, "\t%s\n", sdlExtensions[i]);
+ }
+
+ VkInstanceCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ .pApplicationInfo = &appInfo,
+ .enabledExtensionCount = sdlExtensionCount,
+ .ppEnabledExtensionNames = sdlExtensions,
+ .enabledLayerCount = 0,
+ };
+
+ if (enableValidationLayers) {
+ createInfo.enabledLayerCount = validationLayerCount;
+ createInfo.ppEnabledLayerNames = validationLayers;
+ }
+
+ if (vkCreateInstance(&createInfo, NULL, &s.vk_instance) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "Can't start vulkan instance\n");
+ }
+ vk_log(VK_INFO, "Vulkan instance created\n");
+}
+
+VkExtent2D
+chooseSwapExtent(const VkSurfaceCapabilitiesKHR * capabilities)
+{
+ if (capabilities->currentExtent.width != UINT32_MAX) {
+ return capabilities->currentExtent;
+ } else {
+ int width, height;
+ SDL_GetWindowSize(s.sdl_window, &width, &height);
+
+ VkExtent2D actualExtent;
+ actualExtent.width = (uint32_t) width;
+ actualExtent.height = (uint32_t) height;
+
+ // Manual implementation of std::clamp since it is not available in C
+ actualExtent.width = (actualExtent.width < capabilities->minImageExtent.width) ? capabilities->minImageExtent.width :
+ (actualExtent.width > capabilities->maxImageExtent.width) ? capabilities->maxImageExtent.width :
+ actualExtent.width;
+
+ actualExtent.height = (actualExtent.height < capabilities->minImageExtent.height) ? capabilities->minImageExtent.height :
+ (actualExtent.height > capabilities->maxImageExtent.height) ? capabilities->maxImageExtent.height :
+ actualExtent.height;
+
+ return actualExtent;
+ }
+}
+
+VkSurfaceFormatKHR
+chooseSwapSurfaceFormat(const VkSurfaceFormatKHR * availableFormats, uint32_t formatCount)
+{
+ for (uint32_t i = 0 ; i < formatCount; i ++) {
+ if (availableFormats[i].format == VK_FORMAT_B8G8R8A8_SRGB &&
+ availableFormats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
+ return availableFormats[i];
+ }
+ }
+
+ // if it fails pick the first one
+ return availableFormats[0];
+}
+
+VkPresentModeKHR
+chooseSwapPresentMode(const VkPresentModeKHR * presentModes, uint32_t presentModeCount)
+{
+ for (uint32_t i = 0 ; i < presentModeCount; i ++) {
+ if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
+ return presentModes[i];
+ }
+ }
+
+ // if it fails pick the the FIFO one
+ return VK_PRESENT_MODE_FIFO_KHR;
+}
+
+typedef struct SwapChainSupportDetails {
+ VkSurfaceCapabilitiesKHR capabilities;
+ VkSurfaceFormatKHR formats[100];
+ uint32_t formatCount;
+ VkPresentModeKHR presentModes[100];
+ uint32_t presentModeCount;
+} SwapChainSupportDetails;
+
+SwapChainSupportDetails
+querySwapChainSupport(VkPhysicalDevice device)
+{
+ SwapChainSupportDetails details;
+
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, s.vk_surface, &details.capabilities);
+
+ vkGetPhysicalDeviceSurfaceFormatsKHR(device, s.vk_surface, &details.formatCount, NULL);
+
+ if (details.formatCount != 0) {
+ // todo alloc format arrray
+ vkGetPhysicalDeviceSurfaceFormatsKHR(device, s.vk_surface, &details.formatCount, details.formats);
+ }
+
+ vkGetPhysicalDeviceSurfacePresentModesKHR(device, s.vk_surface, &details.presentModeCount, NULL);
+
+ if (details.presentModeCount != 0) {
+ // todo alloc presentModes array
+ vkGetPhysicalDeviceSurfacePresentModesKHR(device, s.vk_surface, &details.presentModeCount, details.presentModes);
+ }
+
+ return details;
+}
+
+typedef struct QueueFamilyIndices {
+ uint32_t graphicsFamily;
+ bool graphicsFlag;
+ uint32_t presentFamily;
+ bool presentFlag;
+} QueueFamilyIndices;
+
+bool
+vulkan_queue_family_check_flags(QueueFamilyIndices x)
+{
+ return x.graphicsFlag && x.presentFlag;
+}
+
+QueueFamilyIndices vulkan_find_queue_families(VkPhysicalDevice device) {
+ QueueFamilyIndices indices;
+ indices.graphicsFlag = false;
+ indices.presentFlag = false;
+
+ uint32_t queueFamilyCount = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, NULL);
+
+ VkQueueFamilyProperties queueFamilies[queueFamilyCount];
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies);
+
+ for (uint32_t i = 0; i < queueFamilyCount; i++) {
+ if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ indices.graphicsFamily = i;
+ indices.graphicsFlag = true;
+ }
+
+ VkBool32 presentSupport = false;
+ vkGetPhysicalDeviceSurfaceSupportKHR(device, i, s.vk_surface, &presentSupport);
+ if (presentSupport) {
+ indices.presentFamily = i;
+ indices.presentFlag = true;
+ }
+
+ if (vulkan_queue_family_check_flags(indices)) break;
+ }
+
+ return indices;
+}
+
+bool
+vulkan_check_device_extension_support(VkPhysicalDevice device)
+{
+ uint32_t extensionCount;
+ vkEnumerateDeviceExtensionProperties(device, NULL, &extensionCount, NULL);
+
+ VkExtensionProperties availableExtensions[extensionCount];
+ vkEnumerateDeviceExtensionProperties(device, NULL, &extensionCount, availableExtensions);
+
+ uint32_t flag = 0;
+
+ for (uint32_t i = 0; i < deviceExtensionCount; i++) {
+ for (uint32_t j = 0; j < extensionCount; j++) {
+ if (strcmp(deviceExtensions[i], availableExtensions[j].extensionName) == 0) {
+ flag++;
+ break;
+ }
+ }
+ }
+
+ return flag == deviceExtensionCount;
+}
+
+bool
+vulkan_is_device_suitable(VkPhysicalDevice device)
+{
+ QueueFamilyIndices indices = vulkan_find_queue_families(device);
+ bool extensionsSupported = vulkan_check_device_extension_support(device);
+
+ bool swapChainAdequate = false;
+ if (extensionsSupported) {
+ SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
+ swapChainAdequate = !(swapChainSupport.formatCount == 0) && !(swapChainSupport.presentModeCount == 0);
+ }
+
+ return vulkan_queue_family_check_flags(indices) && extensionsSupported && swapChainAdequate;
+}
+
+void
+vulkan_pick_physical_device()
+{
+ uint32_t deviceCount = 0;
+ vkEnumeratePhysicalDevices(s.vk_instance, &deviceCount, NULL);
+ if (deviceCount == 0) {
+ vk_log(VK_INFO, "failed to find GPUs with Vulkan support!\n");
+ }
+
+ VkPhysicalDevice devices[deviceCount];
+ vkEnumeratePhysicalDevices(s.vk_instance, &deviceCount, devices);
+
+ for (uint32_t i = 0; i < deviceCount; i++) {
+ if (vulkan_is_device_suitable(devices[i])) {
+ s.vk_physical_device = devices[i];
+ break;
+ }
+ }
+
+ if (s.vk_physical_device == VK_NULL_HANDLE) {
+ vk_log(VK_ERROR, "failed to find a suitable GPU!\n");
+ }
+
+ VkPhysicalDeviceProperties deviceProperties;
+ vkGetPhysicalDeviceProperties(s.vk_physical_device, &deviceProperties);
+ vk_log(VK_INFO, "Picked [%s] physical device.\n", deviceProperties.deviceName);
+
+ uint32_t extensionCount = 0;
+ vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL);
+ VkExtensionProperties extensions[extensionCount];
+ VkResult result = vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions);
+
+ vk_log(VK_INFO, "Vulkan enabled extensions: %s\n", string_VkResult(result));
+ for (uint32_t i = 0; i < extensionCount; i++) {
+ vk_log(VK_INFO, "\t%s\n", extensions[i].extensionName);
+ }
+
+}
+
+void
+vulkan_create_logical_device()
+{
+ QueueFamilyIndices indices = vulkan_find_queue_families(s.vk_physical_device);
+
+ // TODO CREATE MULPILE QUEUES
+ // https://vulkan-tutorial.com/en/Drawing_a_triangle/Presentation/Window_surface#page_Creating-the-presentation-queue
+
+ VkDeviceQueueCreateInfo queueCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ .queueFamilyIndex = indices.graphicsFamily,
+ .queueCount = 1,
+ };
+
+ float queuePriority = 1.0f;
+ queueCreateInfo.pQueuePriorities = &queuePriority;
+
+ VkPhysicalDeviceFeatures deviceFeatures = {0};
+ vkGetPhysicalDeviceFeatures(s.vk_physical_device, &deviceFeatures);
+#ifndef NDEBUG
+ /* Disable robust buffer access when building without debug */
+ deviceFeatures.robustBufferAccess = VK_FALSE;
+#endif
+
+ VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamic_rendering_feature = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR,
+ .dynamicRendering = VK_TRUE,
+ };
+
+ VkDeviceCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ .pQueueCreateInfos = &queueCreateInfo,
+ .queueCreateInfoCount = 1,
+ .pNext = &dynamic_rendering_feature,
+ .pEnabledFeatures = &deviceFeatures,
+ .enabledExtensionCount = deviceExtensionCount,
+ .ppEnabledExtensionNames = deviceExtensions,
+ .enabledLayerCount = 0,
+ };
+
+ if (vkCreateDevice(s.vk_physical_device, &createInfo, NULL, &s.vk_device) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create logical device!\n");
+ }
+ vk_log(VK_INFO, "Vulkan logical device created\n");
+
+ vkGetDeviceQueue(s.vk_device, indices.graphicsFamily, 0, &s.vk_graphics_queue);
+ vkGetDeviceQueue(s.vk_device, indices.presentFamily, 0, &s.vk_present_queue);
+}
+
+void
+handle_input(bool * quit)
+{
+ SDL_Event e;
+
+ while (SDL_PollEvent(&e) != 0) {
+ // User requests quit
+ if (e.type == SDL_QUIT) {
+ *quit = true;
+ }
+ // User presses a key
+ else if (e.type == SDL_KEYDOWN) {
+ switch (e.key.keysym.sym) {
+ case SDLK_UP:
+ s.x += .1f;
+ break;
+ case SDLK_DOWN:
+ s.x -= .1f;
+ break;
+ case SDLK_LEFT:
+ break;
+ case SDLK_RIGHT:
+ break;
+ }
+ }
+ else if (e.type == SDL_MOUSEWHEEL) {
+ if(e.wheel.y > 0) // scroll up
+ {
+ s.zoom += 1;
+ }
+ else if(e.wheel.y < 0) // scroll down
+ {
+ s.zoom -= 1;
+ if (s.zoom == -100) s.zoom = 1;
+ }
+ }
+ }
+}
+
+void
+vulkan_create_surface()
+{
+ if (SDL_Vulkan_CreateSurface(s.sdl_window, s.vk_instance, &s.vk_surface) == SDL_FALSE) {
+ vk_log(VK_INFO, "Failed to create surface\n");
+ } else {
+ vk_log(VK_INFO, "Vulkan surface created\n");
+ }
+}
+
+void
+vulkan_create_swap_chain()
+{
+ SwapChainSupportDetails swapChainSupport = querySwapChainSupport(s.vk_physical_device);
+
+ VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats, swapChainSupport.formatCount);
+ VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes, swapChainSupport.presentModeCount);
+ VkExtent2D extent = chooseSwapExtent(&swapChainSupport.capabilities);
+
+ uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
+
+ if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
+ imageCount = swapChainSupport.capabilities.maxImageCount;
+ }
+
+ VkSwapchainCreateInfoKHR createInfo = {
+ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+ .surface = s.vk_surface,
+ .minImageCount = imageCount,
+ .imageFormat = surfaceFormat.format,
+ .imageColorSpace = surfaceFormat.colorSpace,
+ .imageExtent = extent,
+ .imageArrayLayers = 1,
+ .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ };
+
+ QueueFamilyIndices indices = vulkan_find_queue_families(s.vk_physical_device);
+
+ 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; // Optional
+ createInfo.pQueueFamilyIndices = NULL; // Optional
+ }
+
+ 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;
+
+ VK_CHECK(vkCreateSwapchainKHR(s.vk_device, &createInfo, NULL, &s.vk_swap_chain));
+ /* if (result != VK_SUCCESS) { */
+ /* vk_log(VK_ERROR, "ERROR: failed to create swap chain! %s\n", string_VkResult(result)); */
+ /* } */
+
+ vkGetSwapchainImagesKHR(s.vk_device, s.vk_swap_chain, &s.vk_swap_chain_image_count, NULL);
+ //vk_log(VK_INFO, "vk_swap_chain_images count: %d\n", s.vk_swap_chain_image_count);
+ // todo alloc space for images
+ vkGetSwapchainImagesKHR(s.vk_device, s.vk_swap_chain, &s.vk_swap_chain_image_count, s.vk_swap_chain_images);
+
+ s.vk_swap_chain_image_format = surfaceFormat.format;
+ s.vk_swap_chain_extent = extent;
+
+ vk_log(VK_INFO, "Vulkan swapchain created!\n");
+}
+
+void
+vulkan_create_image_views()
+{
+ for (size_t i = 0; i < s.vk_swap_chain_image_count; i++) {
+ VkImageViewCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = s.vk_swap_chain_images[i],
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = s.vk_swap_chain_image_format,
+ .components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .components.g = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .components.b = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .components.a = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .subresourceRange.baseMipLevel = 0,
+ .subresourceRange.levelCount = 1,
+ .subresourceRange.baseArrayLayer = 0,
+ .subresourceRange.layerCount = 1,
+ };
+
+ if (vkCreateImageView(s.vk_device, &createInfo, NULL, &s.vk_swap_chain_image_views[i]) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create image views!\n");
+ }
+ }
+ vk_log(VK_INFO, "Vulkan image views created!\n");
+}
+
+VkShaderModule
+createShaderModule(const char * code, long size)
+{
+ VkShaderModuleCreateInfo createInfo = {0};
+ createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ createInfo.codeSize = size;
+ createInfo.pCode = (const uint32_t *)code;
+
+ VkShaderModule shaderModule;
+ if (vkCreateShaderModule(s.vk_device, &createInfo, NULL, &shaderModule) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create shader module!\n");
+ }
+
+ return shaderModule;
+}
+
+shaderc_compilation_result_t
+load_compile_shader_data(const char * path, shaderc_shader_kind shader_kind)
+{
+ FILE* file = fopen(path, "r");
+ if (!file) {
+ vk_log(VK_ERROR, "SHADER COMPILATION: Failed to open file: %s\n", path);
+ return NULL;
+ }
+
+ fseek(file, 0, SEEK_END);
+ long glsl_length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ char* glsl_src = (char*)malloc(glsl_length + 1);
+ if (!glsl_src) {
+ vk_log(VK_ERROR, "SHADER COMPILATION: Failed to allocate memory\n");
+ fclose(file);
+ return NULL;
+ }
+
+ fread(glsl_src, 1, glsl_length, file);
+ glsl_src[glsl_length] = '\0';
+ fclose(file);
+
+ shaderc_compiler_t compiler = shaderc_compiler_initialize();
+ if (!compiler) {
+ vk_log(VK_ERROR, "SHADER COMPILATION: Failed to initialize shader compiler\n");
+ free(glsl_src);
+ return NULL;
+ }
+
+ shaderc_compile_options_t options = shaderc_compile_options_initialize();
+ shaderc_compilation_result_t result = shaderc_compile_into_spv(compiler,
+ glsl_src,
+ glsl_length,
+ shader_kind,
+ path, "main", options);
+
+ if (shaderc_result_get_compilation_status(result) != shaderc_compilation_status_success) {
+ vk_log(VK_ERROR, "SHADER COMPILATION: error: %s\n", shaderc_result_get_error_message(result));
+ shaderc_result_release(result);
+ shaderc_compiler_release(compiler);
+ free(glsl_src);
+ return NULL;
+ }
+
+ shaderc_compiler_release(compiler);
+ free(glsl_src);
+
+ return result;
+}
+
+void
+vulkan_create_graphics_pipeline()
+{
+ shaderc_compilation_result_t vert_result = load_compile_shader_data("shader.vert", shaderc_vertex_shader);
+ if (!vert_result) {
+ vk_log(VK_ERROR, "Can't load vertex shader");
+ }
+ shaderc_compilation_result_t frag_result = load_compile_shader_data("shader.frag", shaderc_fragment_shader);
+ if (!frag_result) {
+ vk_log(VK_ERROR, "Can't load fragment shader");
+ }
+
+ const char * vert_data = shaderc_result_get_bytes(vert_result);
+ long vert_size = shaderc_result_get_length(vert_result);
+ const char * frag_data = shaderc_result_get_bytes(frag_result);
+ long frag_size = shaderc_result_get_length(frag_result);
+
+ vk_log(VK_INFO, "Shaders loaded\n");
+
+ VkShaderModule vertShaderModule = createShaderModule(vert_data, vert_size);
+ VkShaderModule fragShaderModule = createShaderModule(frag_data, frag_size);
+
+ 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";
+
+ 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";
+
+ VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
+
+ 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 = 2;
+ dynamicState.pDynamicStates = dynamicStates;
+
+ VkVertexInputBindingDescription bindingDescription = {0};
+ bindingDescription.binding = 0;
+ bindingDescription.stride = sizeof(Vertex);
+ bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ VkVertexInputAttributeDescription attributeDescriptions[2] = {0};
+ attributeDescriptions[0].binding = 0;
+ attributeDescriptions[0].location = 0;
+ attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
+ attributeDescriptions[0].offset = offsetof(Vertex, pos);
+
+ attributeDescriptions[1].binding = 0;
+ attributeDescriptions[1].location = 1;
+ attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
+ attributeDescriptions[1].offset = offsetof(Vertex, color);
+
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo = {0};
+ vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.vertexBindingDescriptionCount = 1;
+ vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
+ vertexInputInfo.vertexAttributeDescriptionCount = 2;
+ vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions;
+
+ VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
+ 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) s.vk_swap_chain_extent.width;
+ viewport.height = (float) s.vk_swap_chain_extent.height;
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+
+ VkRect2D scissor = {0};
+ scissor.offset = (VkOffset2D){0, 0};
+ scissor.extent = s.vk_swap_chain_extent;
+
+ VkPipelineViewportStateCreateInfo viewportState = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .viewportCount = 1,
+ .pViewports = &viewport,
+ .scissorCount = 1,
+ .pScissors = &scissor,
+ };
+
+ 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_NONE;
+// rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
+ rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ rasterizer.depthBiasEnable = VK_FALSE;
+ rasterizer.depthBiasConstantFactor = 0.0f; // Optional
+ rasterizer.depthBiasClamp = 0.0f; // Optional
+ rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
+
+ 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_TRUE;
+ colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
+ colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+ colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+ colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
+ /* 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; // Optional
+ colorBlending.attachmentCount = 1;
+ colorBlending.pAttachments = &colorBlendAttachment;
+ colorBlending.blendConstants[0] = 0.0f; // Optional
+ colorBlending.blendConstants[1] = 0.0f; // Optional
+ colorBlending.blendConstants[2] = 0.0f; // Optional
+ colorBlending.blendConstants[3] = 0.0f; // Optional
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo = {0};
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.setLayoutCount = 1;
+ pipelineLayoutInfo.pSetLayouts = &s.vk_descriptor_set_layout;
+ pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
+ pipelineLayoutInfo.pPushConstantRanges = NULL; // Optional
+
+ if (vkCreatePipelineLayout(s.vk_device, &pipelineLayoutInfo, NULL, &s.vk_pipeline_layout) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create pipeline layout!\n");
+ }
+
+ 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.pDepthStencilState = NULL; // Optional
+ pipelineInfo.pColorBlendState = &colorBlending;
+ pipelineInfo.pDynamicState = &dynamicState;
+ pipelineInfo.layout = s.vk_pipeline_layout;
+ pipelineInfo.renderPass = s.vk_render_pass;
+ pipelineInfo.subpass = 0;
+ pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
+ pipelineInfo.basePipelineIndex = -1; // Optional
+
+ if (vkCreateGraphicsPipelines(s.vk_device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &s.vk_graphics_pipeline) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create graphics pipeline!\n");
+ }
+
+ shaderc_result_release(vert_result);
+ shaderc_result_release(frag_result);
+
+ vkDestroyShaderModule(s.vk_device, fragShaderModule, NULL);
+ vkDestroyShaderModule(s.vk_device, vertShaderModule, NULL);
+}
+
+void
+vulkan_create_render_pass()
+{
+ VkAttachmentDescription colorAttachment = {0};
+ colorAttachment.format = s.vk_swap_chain_image_format;
+ 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(s.vk_device, &renderPassInfo, NULL, &s.vk_render_pass) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create render pass!\n");
+ }
+}
+
+void
+vulkan_create_framebuffers()
+{
+ s.vk_swap_chain_framebuffers_count = s.vk_swap_chain_image_count;
+ for (size_t i = 0; i < s.vk_swap_chain_image_count; i++) {
+ VkImageView attachments[] = {
+ s.vk_swap_chain_image_views[i]
+ };
+
+ VkFramebufferCreateInfo framebufferInfo = {0};
+ framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ framebufferInfo.renderPass = s.vk_render_pass;
+ framebufferInfo.attachmentCount = 1;
+ framebufferInfo.pAttachments = attachments;
+ framebufferInfo.width = s.vk_swap_chain_extent.width;
+ framebufferInfo.height = s.vk_swap_chain_extent.height;
+ framebufferInfo.layers = 1;
+
+ if (vkCreateFramebuffer(s.vk_device, &framebufferInfo, NULL, &s.vk_swap_chain_framebuffers[i]) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create framebuffer!\n");
+ }
+ }
+
+}
+
+void
+vulkan_create_command_pool()
+{
+ QueueFamilyIndices queueFamilyIndices = vulkan_find_queue_families(s.vk_physical_device);
+
+ 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(s.vk_device, &poolInfo, NULL, &s.vk_command_pool) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create command pool!\n");
+ }
+
+}
+
+void
+vulkan_create_command_buffer()
+{
+ VkCommandBufferAllocateInfo allocInfo = {0};
+ allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ allocInfo.commandPool = s.vk_command_pool;
+ allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ allocInfo.commandBufferCount = MAX_FRAMES_IN_FLIGHT;
+
+ if (vkAllocateCommandBuffers(s.vk_device, &allocInfo, s.vk_command_buffers) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to allocate command buffers!\n");
+ }
+}
+
+void
+recordCommandBuffer(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) {
+ vk_log(VK_ERROR, "failed to begin recording command buffer!\n");
+ }
+
+ VkRenderPassBeginInfo renderPassInfo = {0};
+ renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ renderPassInfo.renderPass = s.vk_render_pass;
+ renderPassInfo.framebuffer = s.vk_swap_chain_framebuffers[imageIndex];
+ renderPassInfo.renderArea.offset = (VkOffset2D){0, 0};
+ renderPassInfo.renderArea.extent = s.vk_swap_chain_extent;
+
+ VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
+ renderPassInfo.clearValueCount = 1;
+ renderPassInfo.pClearValues = &clearColor;
+
+ // TODO DYNAMIC RENDERING
+ /* VkRenderingAttachmentInfo colorAttachment = {}; */
+ /* colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; */
+ /* colorAttachment.imageView = s.vk_swap_chain_image_views[imageIndex]; */
+ /* colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; */
+ /* colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */
+ /* colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; */
+ /* //colorAttachment.clearValue.color = clearColor; */
+
+ /* VkRenderingInfo renderingInfo = {}; */
+ /* renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; */
+ /* renderingInfo.renderArea = (VkRect2D){ {0, 0}, {(float)(s.vk_swap_chain_extent.width), (float)(s.vk_swap_chain_extent.height)} }; */
+ /* renderingInfo.layerCount = 1; */
+ /* renderingInfo.colorAttachmentCount = 1; */
+ /* renderingInfo.pColorAttachments = &colorAttachment; */
+
+ /* vkCmdBeginRendering(commandBuffer, &renderingInfo); */
+ /* .... */
+ /* vkCmdEndRendering(commandBuffer); */
+
+ vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, s.vk_graphics_pipeline);
+
+ VkViewport viewport = {0};
+ viewport.x = 0.0f;
+ viewport.y = 0.0f;
+ viewport.width = (float)(s.vk_swap_chain_extent.width);
+ viewport.height = (float)(s.vk_swap_chain_extent.height);
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+ vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
+
+ VkRect2D scissor = {0};
+ scissor.offset = (VkOffset2D){0, 0};
+ scissor.extent = s.vk_swap_chain_extent;
+ vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
+
+ VkBuffer vertexBuffers[] = {s.vk_vertex_buffer};
+ VkDeviceSize offsets[] = {0};
+ vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
+ vkCmdBindIndexBuffer(commandBuffer, s.vk_index_buffer, 0, VK_INDEX_TYPE_UINT16);
+ vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, s.vk_pipeline_layout, 0, 1, &s.vk_descriptor_sets[currentFrame], 0, NULL);
+
+ vkCmdDrawIndexed(commandBuffer, INDICES_SIZE, 1, 0, 0, 0);
+
+ vkCmdEndRenderPass(commandBuffer);
+
+ if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to record command buffer!\n");
+ }
+}
+
+void
+vulkan_create_sync_objects()
+{
+ 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(s.vk_device, &semaphoreInfo, NULL, &s.imageAvailableSemaphores[i]) != VK_SUCCESS ||
+ vkCreateSemaphore(s.vk_device, &semaphoreInfo, NULL, &s.renderFinishedSemaphores[i]) != VK_SUCCESS ||
+ vkCreateFence(s.vk_device, &fenceInfo, NULL, &s.inFlightFences[i]) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create semaphores!\n");
+ }
+ }
+}
+
+uint32_t
+findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
+{
+ VkPhysicalDeviceMemoryProperties memProperties;
+ vkGetPhysicalDeviceMemoryProperties(s.vk_physical_device, &memProperties);
+
+ for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
+ if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
+ return i;
+ }
+ }
+
+ vk_log(VK_ERROR, "failed to find suitable memory type!\n");
+ return 9999;
+}
+
+void
+createBuffer(VkDeviceSize size,
+ VkBufferUsageFlags usage,
+ VkMemoryPropertyFlags properties,
+ VkBuffer *buffer, VkDeviceMemory *bufferMemory)
+{
+ VkBufferCreateInfo bufferInfo = {0};
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = size;
+ bufferInfo.usage = usage;
+ bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ if (vkCreateBuffer(s.vk_device, &bufferInfo, NULL, buffer) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create buffer!\n");
+ }
+
+ VkMemoryRequirements memRequirements;
+ vkGetBufferMemoryRequirements(s.vk_device, *buffer, &memRequirements);
+
+ VkMemoryAllocateInfo allocInfo = {0};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.allocationSize = memRequirements.size;
+ allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
+
+ // TODO: allocations limited by hardware
+ if (vkAllocateMemory(s.vk_device, &allocInfo, NULL, bufferMemory) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to allocate buffer memory!\n");
+ }
+
+ vkBindBufferMemory(s.vk_device, *buffer, *bufferMemory, 0);
+}
+
+/* void */
+/* vulkan_create_vertex_buffer() */
+/* { */
+/* VkDeviceSize bufferSize = sizeof(vertices[0]) * VERTICES_SIZE; */
+/* createBuffer(bufferSize, */
+/* VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, */
+/* VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, */
+/* &s.vk_vertex_buffer, &s.vk_vertex_buffer_memory); */
+
+/* void* data; */
+/* vkMapMemory(s.vk_device, s.vk_vertex_buffer_memory, 0, bufferSize, 0, &data); */
+/* memcpy(data, vertices, (size_t) bufferSize); */
+/* vkUnmapMemory(s.vk_device, s.vk_vertex_buffer_memory); */
+/* } */
+void
+copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
+ VkCommandBufferAllocateInfo allocInfo = {0};
+ allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ allocInfo.commandPool = s.vk_command_pool;
+ allocInfo.commandBufferCount = 1;
+
+ VkCommandBuffer commandBuffer;
+ vkAllocateCommandBuffers(s.vk_device, &allocInfo, &commandBuffer);
+
+ VkCommandBufferBeginInfo beginInfo = {0};
+ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+
+ vkBeginCommandBuffer(commandBuffer, &beginInfo);
+ VkBufferCopy copyRegion = {0};
+ copyRegion.srcOffset = 0; // Optional
+ copyRegion.dstOffset = 0; // Optional
+ copyRegion.size = size;
+ vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
+ vkEndCommandBuffer(commandBuffer);
+
+ VkSubmitInfo submitInfo = {0};
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &commandBuffer;
+
+ vkQueueSubmit(s.vk_graphics_queue, 1, &submitInfo, VK_NULL_HANDLE);
+ vkQueueWaitIdle(s.vk_graphics_queue);
+
+ vkFreeCommandBuffers(s.vk_device, s.vk_command_pool, 1, &commandBuffer);
+}
+
+
+
+void
+vulkan_create_vertex_buffer()
+{
+ VkDeviceSize bufferSize = sizeof(vertices[0]) * VERTICES_SIZE;
+
+ VkBuffer stagingBuffer;
+ VkDeviceMemory stagingBufferMemory;
+
+ createBuffer(bufferSize,
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ &stagingBuffer, &stagingBufferMemory);
+
+ void* data;
+ vkMapMemory(s.vk_device, stagingBufferMemory, 0, bufferSize, 0, &data);
+ memcpy(data, vertices, (size_t) bufferSize);
+ vkUnmapMemory(s.vk_device, stagingBufferMemory);
+
+ createBuffer(bufferSize,
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ &s.vk_vertex_buffer, &s.vk_vertex_buffer_memory);
+
+ copyBuffer(stagingBuffer, s.vk_vertex_buffer, bufferSize);
+
+ vkDestroyBuffer(s.vk_device, stagingBuffer, NULL);
+ vkFreeMemory(s.vk_device, stagingBufferMemory, NULL);
+}
+
+void
+vulkan_create_index_buffer()
+{
+ VkDeviceSize bufferSize = sizeof(indices[0]) * INDICES_SIZE;
+
+ VkBuffer stagingBuffer;
+ VkDeviceMemory stagingBufferMemory;
+
+ createBuffer(bufferSize,
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ &stagingBuffer, &stagingBufferMemory);
+
+ void* data;
+ vkMapMemory(s.vk_device, stagingBufferMemory, 0, bufferSize, 0, &data);
+ memcpy(data, indices, (size_t) bufferSize);
+ vkUnmapMemory(s.vk_device, stagingBufferMemory);
+
+ createBuffer(bufferSize,
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ &s.vk_index_buffer, &s.vk_index_buffer_memory);
+
+ copyBuffer(stagingBuffer, s.vk_index_buffer, bufferSize);
+
+ vkDestroyBuffer(s.vk_device, stagingBuffer, NULL);
+ vkFreeMemory(s.vk_device, stagingBufferMemory, NULL);
+}
+
+void
+vulkan_create_descriptor_set_layout()
+{
+ VkDescriptorSetLayoutBinding uboLayoutBinding = {0};
+ uboLayoutBinding.binding = 0;
+ uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ uboLayoutBinding.descriptorCount = 1;
+ uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+ uboLayoutBinding.pImmutableSamplers = NULL; // optional
+
+ VkDescriptorSetLayoutCreateInfo layoutInfo = {0};
+ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutInfo.bindingCount = 1;
+ layoutInfo.pBindings = &uboLayoutBinding;
+
+ if (vkCreateDescriptorSetLayout(s.vk_device, &layoutInfo, NULL, &s.vk_descriptor_set_layout) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create descriptor set layout!\n");
+ }
+}
+
+void
+vulkan_create_uniform_buffers()
+{
+ VkDeviceSize bufferSize = sizeof(UniformBufferObject);
+
+ for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+ createBuffer(bufferSize,
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ &s.vk_uniform_buffers[i], &s.vk_uniform_buffers_memory[i]);
+
+ vkMapMemory(s.vk_device, s.vk_uniform_buffers_memory[i], 0, bufferSize, 0, &s.vk_uniform_buffers_mapped[i]);
+ }
+}
+
+void
+vulkan_create_descriptor_pool()
+{
+ VkDescriptorPoolSize poolSize = {0};
+ poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ poolSize.descriptorCount = MAX_FRAMES_IN_FLIGHT;
+
+ VkDescriptorPoolCreateInfo poolInfo = {0};
+ poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ poolInfo.poolSizeCount = 1;
+ poolInfo.pPoolSizes = &poolSize;
+ poolInfo.maxSets = MAX_FRAMES_IN_FLIGHT;
+
+ if (vkCreateDescriptorPool(s.vk_device, &poolInfo, NULL, &s.vk_descriptor_pool) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to create descriptor pool!\n");
+ }
+
+}
+
+void
+vulkan_create_descriptor_sets()
+{
+ VkDescriptorSetLayout layouts[MAX_FRAMES_IN_FLIGHT] = {0};
+
+ for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+ layouts[i] = s.vk_descriptor_set_layout;
+ }
+
+ VkDescriptorSetAllocateInfo allocInfo = {0};
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorPool = s.vk_descriptor_pool;
+ allocInfo.descriptorSetCount = MAX_FRAMES_IN_FLIGHT;
+ allocInfo.pSetLayouts = layouts;
+
+ if (vkAllocateDescriptorSets(s.vk_device, &allocInfo, s.vk_descriptor_sets) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to allocate descriptor sets!\n");
+ }
+
+ for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+ VkDescriptorBufferInfo bufferInfo = {0};
+ bufferInfo.buffer = s.vk_uniform_buffers[i];
+ bufferInfo.offset = 0;
+ bufferInfo.range = sizeof(UniformBufferObject);
+
+ VkWriteDescriptorSet descriptorWrite = {0};
+ descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrite.dstSet = s.vk_descriptor_sets[i];
+ descriptorWrite.dstBinding = 0;
+ descriptorWrite.dstArrayElement = 0;
+ descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ descriptorWrite.descriptorCount = 1;
+ descriptorWrite.pBufferInfo = &bufferInfo;
+ descriptorWrite.pImageInfo = NULL; // Optional
+ descriptorWrite.pTexelBufferView = NULL; // Optional
+
+ vkUpdateDescriptorSets(s.vk_device, 1, &descriptorWrite, 0, NULL);
+ }
+}
+
+void
+init_vulkan()
+{
+ vk_log(VK_WARN, "====================================\n");
+ vk_log(VK_WARN, " DEBUG ON \n");
+ vk_log(VK_WARN, "====================================\n");
+
+ vulkan_create_instance();
+ // vulkan_setup_debug_messenger();
+ vulkan_create_surface();
+ vulkan_pick_physical_device();
+ vulkan_create_logical_device();
+ vulkan_create_swap_chain();
+ vulkan_create_image_views();
+ vulkan_create_render_pass();
+ vulkan_create_descriptor_set_layout();
+ vulkan_create_graphics_pipeline();
+ vulkan_create_framebuffers();
+ vulkan_create_command_pool();
+ vulkan_create_vertex_buffer();
+ vulkan_create_index_buffer();
+ vulkan_create_uniform_buffers();
+ vulkan_create_descriptor_pool();
+ vulkan_create_descriptor_sets();
+ vulkan_create_command_buffer();
+ vulkan_create_sync_objects();
+}
+
+void
+cleanupSwapChain()
+{
+ for (size_t i = 0; i < s.vk_swap_chain_image_count; i++) {
+ vkDestroyFramebuffer(s.vk_device, s.vk_swap_chain_framebuffers[i], NULL);
+ }
+ for (uint32_t i = 0; i < s.vk_swap_chain_image_count; i++) {
+ vkDestroyImageView(s.vk_device, s.vk_swap_chain_image_views[i], NULL);
+ }
+ vkDestroySwapchainKHR(s.vk_device, s.vk_swap_chain, NULL);
+}
+
+void
+recreateSwapChain()
+{
+ vkDeviceWaitIdle(s.vk_device);
+
+ cleanupSwapChain();
+
+ vulkan_create_swap_chain();
+ vulkan_create_image_views();
+ vulkan_create_framebuffers();
+}
+
+void
+close_vulkan()
+{
+ vkDeviceWaitIdle(s.vk_device);
+ // Cleanup
+ for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+ vkDestroySemaphore(s.vk_device, s.imageAvailableSemaphores[i], NULL);
+ vkDestroySemaphore(s.vk_device, s.renderFinishedSemaphores[i], NULL);
+ vkDestroyFence(s.vk_device, s.inFlightFences[i], NULL);
+ }
+
+ vkDestroyCommandPool(s.vk_device, s.vk_command_pool, NULL);
+
+ cleanupSwapChain();
+
+ for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+ vkDestroyBuffer(s.vk_device, s.vk_uniform_buffers[i], NULL);
+ vkFreeMemory(s.vk_device, s.vk_uniform_buffers_memory[i], NULL);
+ }
+ vkDestroyDescriptorPool(s.vk_device, s.vk_descriptor_pool, NULL);
+ vkDestroyDescriptorSetLayout(s.vk_device, s.vk_descriptor_set_layout, NULL);
+
+ vkDestroyBuffer(s.vk_device, s.vk_vertex_buffer, NULL);
+ vkFreeMemory(s.vk_device, s.vk_vertex_buffer_memory, NULL);
+
+ vkDestroyBuffer(s.vk_device, s.vk_index_buffer, NULL);
+ vkFreeMemory(s.vk_device, s.vk_index_buffer_memory, NULL);
+
+ vkDestroyPipeline(s.vk_device, s.vk_graphics_pipeline, NULL);
+ vkDestroyRenderPass(s.vk_device, s.vk_render_pass, NULL);
+ vkDestroyPipelineLayout(s.vk_device, s.vk_pipeline_layout, NULL);
+ vkDestroyDevice(s.vk_device, NULL);
+ vkDestroySurfaceKHR(s.vk_instance, s.vk_surface, NULL);
+ /* if (enableValidationLayers) { */
+ /* DestroyDebugUtilsMessengerEXT(s.vk_instance, s.vk_debug_messenger, NULL); */
+ /* } */
+ vkDestroyInstance(s.vk_instance, NULL);
+}
+
+float
+current_time()
+{
+ static struct timespec startTime;
+ static int isStartTimeInitialized = 0;
+
+ if (!isStartTimeInitialized) {
+ clock_gettime(CLOCK_MONOTONIC, &startTime);
+ isStartTimeInitialized = 1;
+ }
+
+ struct timespec currentTime;
+ clock_gettime(CLOCK_MONOTONIC, &currentTime);
+
+ return (currentTime.tv_sec - startTime.tv_sec) +
+ (currentTime.tv_nsec - startTime.tv_nsec) / 1e9f;
+}
+
+void
+updateUniformBuffer(uint32_t currentImage)
+{
+
+ float time = current_time();
+
+ UniformBufferObject ubo = {0};
+ glm_mat4_identity(ubo.model);
+ glm_rotate(ubo.model, glm_rad(100 * time * glm_rad(90.0f)), GLM_ZUP);
+
+ vec3 eye = {1.0f + s.x, 1.0f + s.x, 1.0f + s.x}; // GLM_VEC3_ONE_INIT;
+ vec3 center = GLM_VEC3_ZERO_INIT;
+ vec3 up = {0.0f, 0.0f, 1.0f};//GLM_ZUP;
+ glm_lookat(eye, center, up, ubo.view);
+
+ float aspect = s.vk_swap_chain_extent.width / (float)s.vk_swap_chain_extent.height;
+ glm_perspective(glm_rad(55.0f - s.zoom ), aspect, 0.1f, 100.0f, ubo.proj);
+
+ // Inverting the Y axis for Vulkan
+ ubo.proj[1][1] *= -1;
+
+ memcpy(s.vk_uniform_buffers_mapped[currentImage], &ubo, sizeof(ubo));
+}
+
+void
+draw_frame() {
+ vkWaitForFences(s.vk_device, 1, &s.inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
+
+ uint32_t imageIndex;
+ VkResult result = vkAcquireNextImageKHR(s.vk_device, s.vk_swap_chain, UINT64_MAX, s.imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
+
+ if (result == VK_ERROR_OUT_OF_DATE_KHR) {
+ recreateSwapChain();
+ return;
+ } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
+ vk_log(VK_ERROR, "failed to acquire swap chain image!\n");
+ }
+
+ updateUniformBuffer(currentFrame);
+
+ vkResetFences(s.vk_device, 1, &s.inFlightFences[currentFrame]);
+
+ vkResetCommandBuffer(s.vk_command_buffers[currentFrame], 0);
+ recordCommandBuffer(s.vk_command_buffers[currentFrame], imageIndex);
+
+
+ VkSubmitInfo submitInfo = {0};
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+
+ VkSemaphore waitSemaphores[] = {s.imageAvailableSemaphores[currentFrame]};
+ VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
+ submitInfo.waitSemaphoreCount = 1;
+ submitInfo.pWaitSemaphores = waitSemaphores;
+ submitInfo.pWaitDstStageMask = waitStages;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &s.vk_command_buffers[currentFrame];
+
+ VkSemaphore signalSemaphores[] = {s.renderFinishedSemaphores[currentFrame]};
+ submitInfo.signalSemaphoreCount = 1;
+ submitInfo.pSignalSemaphores = signalSemaphores;
+
+ if (vkQueueSubmit(s.vk_graphics_queue, 1, &submitInfo, s.inFlightFences[currentFrame]) != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to submit draw command buffer!\n");
+ }
+
+ VkSwapchainKHR swapChains[] = {s.vk_swap_chain};
+ VkPresentInfoKHR presentInfo = {0};
+ presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ presentInfo.waitSemaphoreCount = 1;
+ presentInfo.pWaitSemaphores = signalSemaphores;
+ presentInfo.swapchainCount = 1;
+ presentInfo.pSwapchains = swapChains;
+ presentInfo.pImageIndices = &imageIndex;
+ presentInfo.pResults = NULL;
+
+ result = vkQueuePresentKHR(s.vk_present_queue, &presentInfo);
+
+ if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
+ recreateSwapChain();
+ } else if (result != VK_SUCCESS) {
+ vk_log(VK_ERROR, "failed to present swap chain image!\n");
+ }
+
+ currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
+}
+
+int
+main(int argc, char* args[])
+{
+ init_state(&s);
+ if (!init()) {
+ vk_log(VK_INFO, "Failed to initialize!\n");
+ }
+ else {
+ init_vulkan();
+
+ bool quit = false;
+
+
+ /* VMA POC */
+ /* VmaVulkanFunctions vulkanFunctions = {0}; */
+ /* vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; */
+ /* vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; */
+
+ /* VmaAllocatorCreateInfo allocatorCreateInfo = {0}; */
+ /* allocatorCreateInfo.flags = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT; */
+ /* allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2; */
+ /* allocatorCreateInfo.physicalDevice = s.vk_physical_device; */
+ /* allocatorCreateInfo.device = s.vk_device; */
+ /* allocatorCreateInfo.instance = s.vk_instance; */
+ /* allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; */
+
+ /* VmaAllocator allocator; */
+ /* vmaCreateAllocator(&allocatorCreateInfo, &allocator); */
+
+ /* // Entire program... */
+
+ /* // At the end, don't forget to: */
+ /* vmaDestroyAllocator(allocator); */
+
+ // Game loop
+ while (!quit) {
+ handle_input(&quit);
+ draw_frame();
+ //SDL_Delay(16);
+ }
+
+ close_vulkan();
+ }
+
+ // Free resources and close SDL
+ closeSDL();
+
+ return 0;
+}