1106 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
			
		
		
	
	
			1106 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
| #include "render.h"
 | |
| #include "stdio.h"
 | |
| #include "string.h"
 | |
| #include "pipeline.h"
 | |
| #include "vk_mem_alloc.h"
 | |
| #include "vulkan/vulkan_core.h"
 | |
| 
 | |
| const uint32_t MAX_FRAMES_IN_FLIGHT = 2;
 | |
| 
 | |
| const char * validation_layers[] = {
 | |
|   "VK_LAYER_KHRONOS_validation",
 | |
|   //"VK_LAYER_LUNARG_api_dump",
 | |
|   "VK_LAYER_KHRONOS_synchronization2",
 | |
|   "VK_LAYER_KHRONOS_shader_object",
 | |
| };
 | |
| uint32_t validation_layer_count = sizeof(validation_layers) / sizeof(const char *);
 | |
| 
 | |
| const char * instance_extensions[] = {
 | |
|   VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
 | |
|   VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
 | |
|   VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
 | |
| #ifdef __APPLE__
 | |
|   "VK_EXT_metal_surface",
 | |
| #endif
 | |
|   VK_KHR_SURFACE_EXTENSION_NAME,
 | |
| };
 | |
| uint32_t instance_extension_count = sizeof(instance_extensions) / sizeof(const char *);
 | |
| 
 | |
| const char * device_extensions[] = {
 | |
| #ifdef __APPLE__
 | |
|   "VK_KHR_portability_subset",
 | |
| #endif
 | |
|   VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
 | |
|   VK_KHR_SWAPCHAIN_EXTENSION_NAME,
 | |
|   VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
 | |
| };
 | |
| uint32_t device_extension_count = sizeof(device_extensions) / sizeof(const char *);
 | |
| 
 | |
| VkFormat depth_formats[] = {
 | |
|   VK_FORMAT_D32_SFLOAT,
 | |
|   VK_FORMAT_D32_SFLOAT_S8_UINT,
 | |
|   VK_FORMAT_D24_UNORM_S8_UINT
 | |
| };
 | |
| uint32_t depth_format_count = sizeof(depth_formats) / sizeof(VkFormat);
 | |
| 
 | |
| 
 | |
| 
 | |
| void glfw_error(int error, const char* description) {
 | |
|   fprintf(stderr, "GLFW_ERR: 0x%02x - %s\n", error, description);
 | |
| }
 | |
| 
 | |
| GLFWwindow* init_window() {
 | |
|   glfwInit();
 | |
|   glfwSetErrorCallback(glfw_error);
 | |
| 
 | |
|   glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
 | |
|   glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
 | |
|   GLFWwindow* window = glfwCreateWindow(800, 500, "roleplay", 0, 0);
 | |
| 
 | |
|   return window;
 | |
| }
 | |
| 
 | |
| bool check_validation_layers(const char ** layers, uint32_t num_layers) {
 | |
|   uint32_t layer_count;
 | |
|   VkResult result;
 | |
| 
 | |
|   result = vkEnumerateInstanceLayerProperties(&layer_count, 0);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   VkLayerProperties* available_layers = malloc(sizeof(VkLayerProperties)*layer_count);
 | |
|   result = vkEnumerateInstanceLayerProperties(&layer_count, available_layers);
 | |
| 
 | |
| 
 | |
|   for(uint32_t i = 0; i < num_layers; i++) {
 | |
|     bool found = false;
 | |
|     for(uint32_t j = 0; j < layer_count; j++) {
 | |
|       if(strcmp(layers[i], available_layers[j].layerName) == 0) {
 | |
|         found = true;
 | |
|       }
 | |
|     }
 | |
|     if(found == false) {
 | |
|       free(available_layers);
 | |
|       fprintf(stderr, "Failed to find layer %s\n", layers[i]);
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   free(available_layers);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| VkResult create_instance(VkInstance* instance) {
 | |
|   if(check_validation_layers(validation_layers, validation_layer_count) == false) {
 | |
|     return VK_ERROR_VALIDATION_FAILED_EXT;
 | |
|   }
 | |
| 
 | |
|   VkApplicationInfo app_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
 | |
|     .pApplicationName = "roleplay",
 | |
|     .applicationVersion = VK_MAKE_VERSION(0, 0, 1),
 | |
|     .pEngineName = "roleplay",
 | |
|     .engineVersion = VK_MAKE_VERSION(0, 0, 1),
 | |
|     .apiVersion = VK_API_VERSION_1_2,
 | |
|   };
 | |
| 
 | |
|   uint32_t glfwExtensionCount = 0;
 | |
|   const char** glfwExtensions;
 | |
| 
 | |
|   glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
 | |
| 
 | |
|   const char** requested_extensions = malloc(sizeof(char*)*(glfwExtensionCount + instance_extension_count));
 | |
|   for (uint32_t i = 0; i < glfwExtensionCount; i++) {
 | |
|     requested_extensions[i] = glfwExtensions[i];
 | |
|   }
 | |
| 
 | |
|   for (uint32_t i = 0; i < instance_extension_count; i++) {
 | |
|     requested_extensions[glfwExtensionCount + i] = instance_extensions[i];
 | |
|   }
 | |
| 
 | |
|   VkInstanceCreateInfo instance_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
 | |
|     .pApplicationInfo = &app_info,
 | |
|     .enabledLayerCount = validation_layer_count,
 | |
|     .ppEnabledLayerNames = validation_layers,
 | |
|     .enabledExtensionCount = glfwExtensionCount + instance_extension_count,
 | |
|     .ppEnabledExtensionNames = requested_extensions,
 | |
|     .flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR,
 | |
|   };
 | |
| 
 | |
|   VkResult result = vkCreateInstance(&instance_info, 0, instance);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   free(requested_extensions);
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(
 | |
|   VkDebugUtilsMessageSeverityFlagBitsEXT severity,
 | |
|   VkDebugUtilsMessageTypeFlagsEXT type,
 | |
|   const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
 | |
|   void* user_data) {
 | |
| 
 | |
|   (void)severity;
 | |
|   (void)type;
 | |
|   (void)user_data;
 | |
| 
 | |
|   fprintf(stderr, "Validation layer: %s\n", callback_data->pMessage);
 | |
| 
 | |
|   return VK_FALSE;
 | |
| }
 | |
| 
 | |
| VkResult create_debug_messenger(VkInstance instance, VkDebugUtilsMessengerEXT* debug_messenger) {
 | |
|   VkDebugUtilsMessengerCreateInfoEXT messenger_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
 | |
|     .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
 | |
|     .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT,
 | |
|     .pfnUserCallback = debug_callback,
 | |
|     .pUserData = 0,
 | |
|   };
 | |
| 
 | |
|   PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
 | |
| 
 | |
|   VkResult result;
 | |
|   result = func(instance, &messenger_info, 0, debug_messenger);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult get_best_physical_device(VkInstance instance, VkPhysicalDevice* device) {
 | |
|   uint32_t device_count = 0;
 | |
|   VkResult result;
 | |
|   result = vkEnumeratePhysicalDevices(instance, &device_count, 0);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   VkPhysicalDevice* devices = malloc(sizeof(VkPhysicalDevice)*device_count);
 | |
|   result = vkEnumeratePhysicalDevices(instance, &device_count, devices);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     free(devices);
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   int top_score = -1;
 | |
|   for(uint32_t i = 0; i < device_count; i++) {
 | |
|     int score = 0;
 | |
| 
 | |
|     VkPhysicalDeviceProperties properties;
 | |
|     vkGetPhysicalDeviceProperties(devices[i], &properties);
 | |
| 
 | |
|     VkPhysicalDeviceFeatures features;
 | |
|     vkGetPhysicalDeviceFeatures(devices[i], &features);
 | |
| 
 | |
|     switch(properties.deviceType) {
 | |
|     case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
 | |
|       score += 100;
 | |
|       break;
 | |
|     case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
 | |
|       score += 50;
 | |
|       break;
 | |
|     case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
 | |
|       score += 25;
 | |
|       break;
 | |
|     case VK_PHYSICAL_DEVICE_TYPE_CPU:
 | |
|       score += 0;
 | |
|       break;
 | |
|     default:
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if(score > top_score) {
 | |
|       top_score = score;
 | |
|       *device = devices[i];
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   free(devices);
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult create_logical_device(VkPhysicalDevice physical_device, VkSurfaceKHR surface, Queue* graphics_queue, Queue* present_queue, Queue* transfer_queue, VkDevice* device) {
 | |
|   if(graphics_queue == NULL || present_queue == NULL || transfer_queue == NULL || device == NULL) {
 | |
|     return VK_ERROR_VALIDATION_FAILED_EXT;
 | |
|   }
 | |
| 
 | |
|   uint32_t queue_family_count = 0;
 | |
|   vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, NULL);
 | |
| 
 | |
|   VkQueueFamilyProperties* queue_families = malloc(sizeof(VkQueueFamilyProperties)*queue_family_count);
 | |
|   vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families);
 | |
| 
 | |
|   graphics_queue->family = 0xFFFFFFFF;
 | |
|   present_queue->family = 0xFFFFFFFF;
 | |
|   for(uint32_t idx = 0; idx < queue_family_count; idx++) {
 | |
|     VkBool32 present_support = VK_FALSE;
 | |
|     vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, idx, surface, &present_support);
 | |
|     VkBool32 graphics_support = (queue_families[idx].queueFlags & VK_QUEUE_GRAPHICS_BIT);
 | |
| 
 | |
|     if(graphics_support && present_support) {
 | |
|       graphics_queue->family = idx;
 | |
|       graphics_queue->index = 0;
 | |
| 
 | |
|       present_queue->family = idx;
 | |
|       present_queue->index = 0;
 | |
|       break;
 | |
|     } else if (graphics_support && (graphics_queue->family == 0xFFFFFFFF)) {
 | |
|       graphics_queue->family = idx;
 | |
|       graphics_queue->index = 0;
 | |
|     } else if (present_support && (present_queue->family == 0xFFFFFFFF)) {
 | |
|       graphics_queue->family = idx;
 | |
|       present_queue->index = 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   transfer_queue->family = 0xFFFFFFFF;
 | |
|   for(uint32_t idx = 0; idx < queue_family_count; idx++) {
 | |
|     VkBool32 graphics_support = (queue_families[idx].queueFlags & VK_QUEUE_GRAPHICS_BIT);
 | |
|     VkBool32 compute_support = (queue_families[idx].queueFlags & VK_QUEUE_COMPUTE_BIT);
 | |
|     VkBool32 is_graphics_family = (graphics_queue->family == idx);
 | |
|     VkBool32 is_present_family = (present_queue->family == idx);
 | |
|     uint32_t queue_count = queue_families[idx].queueCount;
 | |
| 
 | |
|     if(is_graphics_family && (queue_count == 1)) {
 | |
|       continue;
 | |
|     } else if (is_present_family && (queue_count == 1)) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if(graphics_support && compute_support) {
 | |
|       transfer_queue->family = idx;
 | |
|       if(is_graphics_family || is_present_family) {
 | |
|         transfer_queue->index = 1;
 | |
|       } else {
 | |
|         transfer_queue->index = 0;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if(graphics_queue->family == 0xFFFFFFFF || present_queue->family == 0xFFFFFFFF || transfer_queue->family == 0xFFFFFFFF) {
 | |
|     return VK_ERROR_INITIALIZATION_FAILED;
 | |
|   }
 | |
| 
 | |
|   VkDeviceQueueCreateInfo queue_create_info[3] = {};
 | |
|   uint32_t queue_count = 0;
 | |
|   float default_queue_priority = 1.0f;
 | |
|   if(graphics_queue->family == present_queue->family && graphics_queue->family == transfer_queue->family) {
 | |
|     queue_count = 1;
 | |
| 
 | |
|     queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | |
|     queue_create_info[0].queueFamilyIndex = graphics_queue->family;
 | |
|     queue_create_info[0].queueCount = 2;
 | |
|     queue_create_info[0].pQueuePriorities = &default_queue_priority;
 | |
|   } else if (graphics_queue->family == present_queue->family) {
 | |
|     queue_count = 2;
 | |
| 
 | |
|     queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | |
|     queue_create_info[0].queueFamilyIndex = graphics_queue->family;
 | |
|     queue_create_info[0].queueCount = 1;
 | |
|     queue_create_info[0].pQueuePriorities = &default_queue_priority;
 | |
| 
 | |
|     queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | |
|     queue_create_info[1].queueFamilyIndex = transfer_queue->family;
 | |
|     queue_create_info[1].queueCount = 1;
 | |
|     queue_create_info[1].pQueuePriorities = &default_queue_priority;
 | |
|   } else if (graphics_queue->family == transfer_queue->family) {
 | |
|     queue_count = 2;
 | |
| 
 | |
|     queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | |
|     queue_create_info[0].queueFamilyIndex = graphics_queue->family;
 | |
|     queue_create_info[0].queueCount = 2;
 | |
|     queue_create_info[0].pQueuePriorities = &default_queue_priority;
 | |
| 
 | |
|     queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | |
|     queue_create_info[1].queueFamilyIndex = present_queue->family;
 | |
|     queue_create_info[1].queueCount = 1;
 | |
|     queue_create_info[1].pQueuePriorities = &default_queue_priority;
 | |
|   } else {
 | |
|     queue_count = 3;
 | |
| 
 | |
|     queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | |
|     queue_create_info[0].queueFamilyIndex = graphics_queue->family;
 | |
|     queue_create_info[0].queueCount = 1;
 | |
|     queue_create_info[0].pQueuePriorities = &default_queue_priority;
 | |
| 
 | |
|     queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | |
|     queue_create_info[1].queueFamilyIndex = present_queue->family;
 | |
|     queue_create_info[1].queueCount = 1;
 | |
|     queue_create_info[1].pQueuePriorities = &default_queue_priority;
 | |
| 
 | |
|     queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | |
|     queue_create_info[1].queueFamilyIndex = transfer_queue->family;
 | |
|     queue_create_info[1].queueCount = 1;
 | |
|     queue_create_info[1].pQueuePriorities = &default_queue_priority;
 | |
|   }
 | |
| 
 | |
|   VkPhysicalDeviceVulkan12Features features_12 = {
 | |
|     .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
 | |
|     .bufferDeviceAddress = VK_TRUE,
 | |
|     .descriptorIndexing = VK_TRUE,
 | |
|     .descriptorBindingPartiallyBound = VK_TRUE,
 | |
|     .descriptorBindingVariableDescriptorCount = VK_TRUE,
 | |
|     .descriptorBindingUniformBufferUpdateAfterBind = VK_TRUE,
 | |
|     .descriptorBindingStorageBufferUpdateAfterBind = VK_TRUE,
 | |
|     .descriptorBindingSampledImageUpdateAfterBind = VK_TRUE,
 | |
|   };
 | |
| 
 | |
|   VkPhysicalDeviceFeatures device_features = {
 | |
|     .samplerAnisotropy = VK_TRUE,
 | |
|   };
 | |
| 
 | |
|   VkDeviceCreateInfo device_create_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
 | |
|     .pQueueCreateInfos = queue_create_info,
 | |
|     .queueCreateInfoCount = queue_count,
 | |
|     .pEnabledFeatures = &device_features,
 | |
|     .enabledExtensionCount = device_extension_count,
 | |
|     .ppEnabledExtensionNames = device_extensions,
 | |
|     .enabledLayerCount = validation_layer_count,
 | |
|     .ppEnabledLayerNames = validation_layers,
 | |
|     .pNext = &features_12,
 | |
|   };
 | |
| 
 | |
|   VkResult result = vkCreateDevice(physical_device, &device_create_info, 0, device);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   vkGetDeviceQueue(*device, graphics_queue->family, graphics_queue->index, &graphics_queue->handle);
 | |
|   vkGetDeviceQueue(*device, present_queue->family, present_queue->index, &present_queue->handle);
 | |
|   vkGetDeviceQueue(*device, transfer_queue->family, transfer_queue->index, &transfer_queue->handle);
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult create_memory_allocator(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, VmaAllocator* allocator) {
 | |
|   VmaAllocatorCreateInfo allocator_create_info = {
 | |
|     .vulkanApiVersion = VK_API_VERSION_1_2,
 | |
|     .instance = instance,
 | |
|     .physicalDevice = physical_device,
 | |
|     .device = device,
 | |
|     .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
 | |
|   };
 | |
| 
 | |
|   VkResult result = vmaCreateAllocator(&allocator_create_info, allocator);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult get_swapchain_details(VkPhysicalDevice physical_device, VkSurfaceKHR surface, SwapchainDetails* details) {
 | |
|   details->formats = 0;
 | |
|   details->present_modes = 0;
 | |
| 
 | |
|   VkResult result;
 | |
| 
 | |
|   result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &details->capabilities);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &details->formats_count, 0);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
|   details->formats = malloc(sizeof(VkSurfaceFormatKHR)*details->formats_count);
 | |
|   result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &details->formats_count, details->formats);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     free(details->formats);
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &details->present_modes_count, 0);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     free(details->formats);
 | |
|     return result;
 | |
|   }
 | |
|   details->present_modes = malloc(sizeof(VkPresentModeKHR)*details->present_modes_count);
 | |
|   result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &details->present_modes_count, details->present_modes);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     free(details->formats);
 | |
|     free(details->present_modes);
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkSurfaceFormatKHR choose_swapchain_format(SwapchainDetails swapchain_details) {
 | |
|   for(uint32_t i = 0; i < swapchain_details.formats_count; i++) {
 | |
|     VkSurfaceFormatKHR format = swapchain_details.formats[i];
 | |
|     if(format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
 | |
|       return format;
 | |
|     }
 | |
|   }
 | |
|   return swapchain_details.formats[0];
 | |
| }
 | |
| 
 | |
| VkPresentModeKHR choose_present_mode(SwapchainDetails swapchain_details) {
 | |
|   for(uint32_t i = 0; i < swapchain_details.present_modes_count; i++) {
 | |
|     if(swapchain_details.present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
 | |
|       return VK_PRESENT_MODE_MAILBOX_KHR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return VK_PRESENT_MODE_FIFO_KHR;
 | |
| }
 | |
| 
 | |
| VkExtent2D choose_swapchain_extent(SwapchainDetails swapchain_details) {
 | |
|   return swapchain_details.capabilities.currentExtent;
 | |
| }
 | |
| 
 | |
| VkResult create_swapchain(VkDevice device, VkSurfaceFormatKHR format, VkPresentModeKHR present_mode, VkExtent2D extent, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR capabilities, uint32_t graphics_family_index, uint32_t present_family_index, VkSwapchainKHR* swapchain) {
 | |
|   uint32_t image_count = capabilities.minImageCount + 1;
 | |
|   uint32_t max_images = capabilities.maxImageCount;
 | |
|   if((max_images > 0) && (image_count > max_images)) {
 | |
|     image_count = max_images;
 | |
|   }
 | |
| 
 | |
|   VkSwapchainCreateInfoKHR swapchain_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
 | |
|     .surface = surface,
 | |
|     .minImageCount = image_count,
 | |
|     .imageFormat = format.format,
 | |
|     .imageColorSpace = format.colorSpace,
 | |
|     .imageExtent = extent,
 | |
|     .imageArrayLayers = 1,
 | |
|     .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
 | |
|     .preTransform = capabilities.currentTransform,
 | |
|     .compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
 | |
|     .presentMode = present_mode,
 | |
|     .clipped = VK_TRUE,
 | |
|     .oldSwapchain = *swapchain,
 | |
|   };
 | |
| 
 | |
|   uint32_t queue_families[2] = {graphics_family_index, present_family_index};
 | |
|   if(graphics_family_index != present_family_index) {
 | |
|     swapchain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
 | |
|     swapchain_info.queueFamilyIndexCount = 2;
 | |
|     swapchain_info.pQueueFamilyIndices = queue_families;
 | |
|   } else {
 | |
|     swapchain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
 | |
|     swapchain_info.queueFamilyIndexCount = 0;
 | |
|     swapchain_info.pQueueFamilyIndices = 0;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   VkResult result;
 | |
|   result = vkCreateSwapchainKHR(device, &swapchain_info, 0, swapchain);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult get_swapchain_images(VkDevice device, VkSwapchainKHR swapchain, VkImage** images, uint32_t* image_count) {
 | |
|   VkResult result;
 | |
|   result = vkGetSwapchainImagesKHR(device, swapchain, image_count, 0);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   *images = malloc(sizeof(VkImage)*(*image_count));
 | |
|   if(*images == 0) {
 | |
|     return VK_ERROR_OUT_OF_HOST_MEMORY;
 | |
|   }
 | |
| 
 | |
|   result = vkGetSwapchainImagesKHR(device, swapchain, image_count, *images);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     free(*images);
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult create_image_views(VkDevice device, uint32_t image_count, VkImage* images, VkSurfaceFormatKHR format, VkImageView** image_views) {
 | |
|   *image_views = malloc(sizeof(VkImageView)*image_count);
 | |
|   if(*image_views == 0) {
 | |
|     return VK_ERROR_OUT_OF_HOST_MEMORY;
 | |
|   }
 | |
| 
 | |
|   for(uint32_t i = 0; i < image_count; i++) {
 | |
|     VkImageViewCreateInfo view_info = {
 | |
|       .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
 | |
|       .image = images[i],
 | |
|       .viewType = VK_IMAGE_VIEW_TYPE_2D,
 | |
|       .format = format.format,
 | |
|       .components = {
 | |
|         .r = VK_COMPONENT_SWIZZLE_IDENTITY,
 | |
|         .g = VK_COMPONENT_SWIZZLE_IDENTITY,
 | |
|         .b = VK_COMPONENT_SWIZZLE_IDENTITY,
 | |
|         .a = VK_COMPONENT_SWIZZLE_IDENTITY,
 | |
|       },
 | |
|       .subresourceRange = {
 | |
|         .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
 | |
|         .baseMipLevel = 0,
 | |
|         .levelCount = 1,
 | |
|         .baseArrayLayer = 0,
 | |
|         .layerCount = 1,
 | |
|       },
 | |
|     };
 | |
| 
 | |
|     VkResult result = vkCreateImageView(device, &view_info, 0, &(*image_views)[i]);
 | |
|     if(result != VK_SUCCESS) {
 | |
|       free(*image_views);
 | |
|       return result;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult find_depth_format(VkPhysicalDevice physical_device, VkImageTiling tiling, VkFormatFeatureFlags features, VkFormat* format) {
 | |
|   for(uint32_t i = 0; i < depth_format_count; i++) {
 | |
|     VkFormatProperties properties;
 | |
|     vkGetPhysicalDeviceFormatProperties(physical_device, depth_formats[i], &properties);
 | |
| 
 | |
|     if(tiling == VK_IMAGE_TILING_LINEAR && (properties.linearTilingFeatures & features) == features) {
 | |
|       *format = depth_formats[i];
 | |
|       return VK_SUCCESS;
 | |
|     } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (properties.optimalTilingFeatures & features) == features) {
 | |
|       *format = depth_formats[i];
 | |
|       return VK_SUCCESS;
 | |
|     }
 | |
|   }
 | |
|   return VK_ERROR_UNKNOWN;
 | |
| }
 | |
| 
 | |
| VkResult create_render_pass(VkDevice device, VkSurfaceFormatKHR format, VkFormat depth_format, VkRenderPass* render_pass) {
 | |
|   VkAttachmentDescription attachments[] = {
 | |
|     {
 | |
|       .format = format.format,
 | |
|       .samples = VK_SAMPLE_COUNT_1_BIT,
 | |
|       .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
 | |
|       .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
 | |
|       .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
 | |
|       .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
 | |
|       .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
 | |
|       .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
 | |
|     },
 | |
|     {
 | |
|       .format = depth_format,
 | |
|       .samples = VK_SAMPLE_COUNT_1_BIT,
 | |
|       .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
 | |
|       .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
 | |
|       .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
 | |
|       .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
 | |
|       .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
 | |
|       .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
 | |
|     },
 | |
|   };
 | |
| 
 | |
|   VkAttachmentReference color_attachment_refs[] = {
 | |
|     {
 | |
|       .attachment = 0,
 | |
|       .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
 | |
|     },
 | |
|   };
 | |
| 
 | |
|   VkAttachmentReference depth_attachment_ref = {
 | |
|     .attachment = 1,
 | |
|     .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
 | |
|   };
 | |
| 
 | |
|   // Create a subpass with the color and depth attachments
 | |
|   VkSubpassDescription subpasses[] = {
 | |
|     {
 | |
|       .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
 | |
|       .colorAttachmentCount = sizeof(color_attachment_refs)/sizeof(VkAttachmentReference),
 | |
|       .pColorAttachments = color_attachment_refs,
 | |
|       .pDepthStencilAttachment = &depth_attachment_ref,
 | |
|     },
 | |
|     {
 | |
|       .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
 | |
|       .colorAttachmentCount = sizeof(color_attachment_refs)/sizeof(VkAttachmentReference),
 | |
|       .pColorAttachments = color_attachment_refs,
 | |
|       .pDepthStencilAttachment = &depth_attachment_ref,
 | |
|     },
 | |
|   };
 | |
| 
 | |
|   // This basically says "make sure nothing else is writing to the depth_stencil or the color attachment during the pipeline
 | |
|   VkSubpassDependency dependencies[] = {
 | |
|     {
 | |
|       .srcSubpass = VK_SUBPASS_EXTERNAL,
 | |
|       .dstSubpass = 0,
 | |
|       .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
 | |
|       .srcAccessMask = 0,
 | |
|       .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
 | |
|       .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
 | |
|       .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT,
 | |
|     },
 | |
|     {
 | |
|       .srcSubpass = 0,
 | |
|       .dstSubpass = 1,
 | |
|       .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
 | |
|       .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
 | |
|       .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
 | |
|       .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
 | |
|       .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT,
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   VkRenderPassCreateInfo render_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
 | |
|     .attachmentCount = sizeof(attachments)/sizeof(VkAttachmentDescription),
 | |
|     .pAttachments = attachments,
 | |
|     .subpassCount = sizeof(subpasses)/sizeof(VkSubpassDescription),
 | |
|     .pSubpasses = subpasses,
 | |
|     .dependencyCount = sizeof(dependencies)/sizeof(VkSubpassDependency),
 | |
|     .pDependencies = dependencies,
 | |
|   };
 | |
| 
 | |
|   VkResult result = vkCreateRenderPass(device, &render_info, 0, render_pass);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult create_swapchain_framebuffers(VkDevice device, uint32_t image_count, VkImageView* image_views, VkImageView depth_image_view, VkRenderPass render_pass, VkExtent2D extent, VkFramebuffer** framebuffers) {
 | |
|   *framebuffers = malloc(sizeof(VkFramebuffer)*image_count);
 | |
|   if(*framebuffers == 0) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   VkFramebuffer* framebuffer_ptr = *framebuffers;
 | |
| 
 | |
|   for(uint32_t i = 0; i < image_count; i++) {
 | |
|     VkImageView attachments[] = {
 | |
|       image_views[i],
 | |
|       depth_image_view,
 | |
|     };
 | |
| 
 | |
|     VkFramebufferCreateInfo framebuffer_info = {
 | |
|       .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
 | |
|       .renderPass = render_pass,
 | |
|       .attachmentCount = 2,
 | |
|       .pAttachments = attachments,
 | |
|       .width = extent.width,
 | |
|       .height = extent.height,
 | |
|       .layers = 1,
 | |
|     };
 | |
| 
 | |
|     VkResult result = vkCreateFramebuffer(device, &framebuffer_info, 0, &framebuffer_ptr[i]);
 | |
|     if(result != VK_SUCCESS) {
 | |
|       free(*framebuffers);
 | |
|       return result;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkSemaphore* create_semaphores(VkDevice device, VkSemaphoreCreateFlags flags, uint32_t count) {
 | |
|   VkSemaphore* semaphores = malloc(sizeof(VkSemaphore)*count);
 | |
|   if(semaphores == 0) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   VkSemaphoreCreateInfo semaphore_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
 | |
|     .flags = flags,
 | |
|   };
 | |
| 
 | |
|   for(uint32_t i = 0; i < count; i++) {
 | |
|     VkResult result = vkCreateSemaphore(device, &semaphore_info, 0, &semaphores[i]);
 | |
|     if(result != VK_SUCCESS) {
 | |
|       free(semaphores);
 | |
|       return 0;
 | |
|     }
 | |
|   }
 | |
|   return semaphores;
 | |
| }
 | |
| 
 | |
| VkFence* create_fences(VkDevice device, VkFenceCreateFlags flags, uint32_t count) {
 | |
|   VkFence* fences = malloc(sizeof(VkFence)*count);
 | |
|   if(fences == 0) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   VkFenceCreateInfo fence_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
 | |
|     .flags = flags,
 | |
|   };
 | |
| 
 | |
|   for(uint32_t i = 0; i < count; i++) {
 | |
|     VkResult result = vkCreateFence(device, &fence_info, 0, &fences[i]);
 | |
|     if(result != VK_SUCCESS) {
 | |
|       free(fences);
 | |
|       return 0;
 | |
|     }
 | |
|   }
 | |
|   return fences;
 | |
| }
 | |
| 
 | |
| VkCommandBuffer* create_command_buffers(VkDevice device, VkCommandPool command_pool, uint32_t image_count)  {
 | |
|   VkCommandBufferAllocateInfo alloc_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
 | |
|     .commandPool = command_pool,
 | |
|     .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
 | |
|     .commandBufferCount = image_count,
 | |
|   };
 | |
| 
 | |
|   VkCommandBuffer* command_buffers = malloc(sizeof(VkCommandBuffer)*image_count);
 | |
|   if(command_buffers == 0) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   VkResult result = vkAllocateCommandBuffers(device, &alloc_info, command_buffers);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return VK_NULL_HANDLE;
 | |
|   }
 | |
| 
 | |
|   return command_buffers;
 | |
| }
 | |
| 
 | |
| VkResult create_depth_image(VkDevice device, VkFormat depth_format, VkExtent2D swapchain_extent, VmaAllocator allocator, VkCommandPool extra_graphics_pool, Queue graphics_queue, VkImage* depth_image, VmaAllocation* depth_image_memory, VkImageView* depth_image_view) {
 | |
|   
 | |
|     VkExtent3D depth_extent = {
 | |
|     .width = swapchain_extent.width,
 | |
|     .height = swapchain_extent.height,
 | |
|     .depth = 1,
 | |
|   };
 | |
| 
 | |
|   VkImageCreateInfo depth_image_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
 | |
|     .imageType = VK_IMAGE_TYPE_2D,
 | |
|     .extent = depth_extent,
 | |
|     .mipLevels = 1,
 | |
|     .arrayLayers = 1,
 | |
|     .format = depth_format,
 | |
|     .tiling = VK_IMAGE_TILING_OPTIMAL,
 | |
|     .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
 | |
|     .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
 | |
|     .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
 | |
|     .samples = VK_SAMPLE_COUNT_1_BIT,
 | |
|     .flags = 0,
 | |
|   };
 | |
|  
 | |
|   VmaAllocationCreateInfo allocation_info = {
 | |
|     .usage = VMA_MEMORY_USAGE_AUTO,
 | |
|   };
 | |
| 
 | |
|   VkResult result = vmaCreateImage(allocator, &depth_image_info, &allocation_info, depth_image, depth_image_memory, NULL);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   VkImageViewCreateInfo depth_view_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
 | |
|     .image = *depth_image,
 | |
|     .viewType = VK_IMAGE_VIEW_TYPE_2D,
 | |
|     .format = depth_format,
 | |
|     .components = {
 | |
|       .r = VK_COMPONENT_SWIZZLE_IDENTITY,
 | |
|       .g = VK_COMPONENT_SWIZZLE_IDENTITY,
 | |
|       .b = VK_COMPONENT_SWIZZLE_IDENTITY,
 | |
|       .a = VK_COMPONENT_SWIZZLE_IDENTITY,
 | |
|     },
 | |
|     .subresourceRange = {
 | |
|       .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
 | |
|       .baseMipLevel = 0,
 | |
|       .levelCount = 1,
 | |
|       .baseArrayLayer = 0,
 | |
|       .layerCount = 1,
 | |
|     },
 | |
|   };
 | |
| 
 | |
|   result = vkCreateImageView(device, &depth_view_info, 0, depth_image_view);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = command_transition_image_layout(device, extra_graphics_pool, graphics_queue, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, *depth_image, 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, VK_IMAGE_ASPECT_DEPTH_BIT);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult init_vulkan(GLFWwindow* window, RenderContext* context) {
 | |
|   VkResult result;
 | |
|   if(context == NULL) {
 | |
|     return VK_ERROR_VALIDATION_FAILED_EXT;
 | |
|   }
 | |
| 
 | |
|   result = create_instance(&context->instance);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = create_debug_messenger(context->instance, &context->debug_messenger);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = get_best_physical_device(context->instance, &context->physical_device);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   vkGetPhysicalDeviceMemoryProperties(context->physical_device, &context->memories);
 | |
| 
 | |
|   result = glfwCreateWindowSurface(context->instance, window, 0, &context->surface);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = create_logical_device(context->physical_device, context->surface, &context->graphics_queue, &context->present_queue, &context->transfer_queue, &context->device);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = create_memory_allocator(context->instance, context->physical_device, context->device, &context->allocator);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   VkCommandPoolCreateInfo extra_pool_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
 | |
|     .queueFamilyIndex = context->graphics_queue.family,
 | |
|     .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
 | |
|   };
 | |
|   result = vkCreateCommandPool(context->device, &extra_pool_info, 0, &context->extra_graphics_pool);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   VkCommandPoolCreateInfo graphics_pool_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
 | |
|     .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
 | |
|     .queueFamilyIndex = context->graphics_queue.family,
 | |
|   };
 | |
|   result = vkCreateCommandPool(context->device, &graphics_pool_info, 0, &context->graphics_pool);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   VkCommandPoolCreateInfo transfer_pool_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
 | |
|     .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
 | |
|     .queueFamilyIndex = context->transfer_queue.family,
 | |
|   };
 | |
|   result = vkCreateCommandPool(context->device, &transfer_pool_info, 0, &context->transfer_pool);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   context->swapchain_command_buffers = create_command_buffers(context->device, context->graphics_pool, MAX_FRAMES_IN_FLIGHT);
 | |
|   if(context->swapchain_command_buffers == NULL) {
 | |
|     return VK_ERROR_UNKNOWN;
 | |
|   }
 | |
| 
 | |
|   context->image_available_semaphores = create_semaphores(context->device, 0, MAX_FRAMES_IN_FLIGHT);
 | |
|   if(context->image_available_semaphores == NULL) {
 | |
|     return VK_ERROR_UNKNOWN;
 | |
|   }
 | |
| 
 | |
|   context->render_finished_semaphores = create_semaphores(context->device, 0, MAX_FRAMES_IN_FLIGHT);
 | |
|   if(context->render_finished_semaphores == NULL) {
 | |
|     return VK_ERROR_UNKNOWN;
 | |
|   }
 | |
| 
 | |
|   context->in_flight_fences = create_fences(context->device, VK_FENCE_CREATE_SIGNALED_BIT, MAX_FRAMES_IN_FLIGHT);
 | |
|   if(context->in_flight_fences == NULL) {
 | |
|     return VK_ERROR_UNKNOWN;
 | |
|   }
 | |
| 
 | |
|   result = get_swapchain_details(context->physical_device, context->surface, &context->swapchain_details);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   context->swapchain_format = choose_swapchain_format(context->swapchain_details);
 | |
|   context->swapchain_present_mode = choose_present_mode(context->swapchain_details);
 | |
|   context->swapchain_extent = choose_swapchain_extent(context->swapchain_details);
 | |
|   context->swapchain = VK_NULL_HANDLE;
 | |
|   result = create_swapchain(context->device, context->swapchain_format, context->swapchain_present_mode, context->swapchain_extent, context->surface, context->swapchain_details.capabilities, context->graphics_queue.family, context->present_queue.family, &context->swapchain);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = get_swapchain_images(context->device, context->swapchain, &context->swapchain_images, &context->swapchain_image_count);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = create_image_views(context->device, context->swapchain_image_count, context->swapchain_images, context->swapchain_format, &context->swapchain_image_views);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = find_depth_format(context->physical_device, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, &context->depth_format);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = create_render_pass(context->device, context->swapchain_format, context->depth_format, &context->render_pass);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = create_depth_image(context->device, context->depth_format, context->swapchain_extent, context->allocator, context->extra_graphics_pool, context->graphics_queue, &context->depth_image, &context->depth_image_memory, &context->depth_image_view);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = create_swapchain_framebuffers(context->device, context->swapchain_image_count, context->swapchain_image_views, context->depth_image_view, context->render_pass, context->swapchain_extent, &context->swapchain_framebuffers);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   context->current_frame = 0;
 | |
|   return VK_SUCCESS;
 | |
| }
 | |
| 
 | |
| VkResult draw_frame(RenderContext* context, UIContext* ui_context, UILayer* ui_layers, uint32_t ui_layer_count) {
 | |
|   VkResult result;
 | |
| 
 | |
|   result = vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   result = vkResetFences(context->device, 1, &context->in_flight_fences[context->current_frame]);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   uint32_t image_index;
 | |
|   VkCommandBuffer command_buffer = context->swapchain_command_buffers[context->current_frame];
 | |
|   result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphores[context->current_frame], VK_NULL_HANDLE, &image_index);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   result = vkResetCommandBuffer(command_buffer, 0);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   VkCommandBufferBeginInfo begin_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
 | |
|   }; 
 | |
|   result = vkBeginCommandBuffer(command_buffer, &begin_info);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   VkViewport viewport = {
 | |
|     .width = context->swapchain_extent.width,
 | |
|     .height = context->swapchain_extent.height,
 | |
|     .maxDepth = 1.0f,
 | |
|     .minDepth = 0.0f,
 | |
|   };
 | |
|   vkCmdSetViewport(command_buffer, 0, 1, &viewport);
 | |
| 
 | |
|   VkRect2D scissor = {
 | |
|     .extent = context->swapchain_extent,
 | |
|   };
 | |
|   vkCmdSetScissor(command_buffer, 0, 1, &scissor);
 | |
| 
 | |
|   VkClearValue clear_values[2] = {{.color={{0.0f, 0.0f, 0.0f, 1.0f}}}, {.depthStencil={1.0f, 0.0f}}};
 | |
|   VkDeviceSize offset = 0;
 | |
| 
 | |
|   VkRenderPassBeginInfo render_pass_begin = {
 | |
|     .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
 | |
|     .renderPass = context->render_pass,
 | |
|     .framebuffer = context->swapchain_framebuffers[image_index],
 | |
|     .renderArea.offset = {0, 0},
 | |
|     .renderArea.extent = context->swapchain_extent,
 | |
|     .clearValueCount = 2,
 | |
|     .pClearValues = clear_values,
 | |
|   };
 | |
|   vkCmdBeginRenderPass(command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
 | |
|   // World subpass
 | |
| 
 | |
|   vkCmdNextSubpass(command_buffer, VK_SUBPASS_CONTENTS_INLINE);
 | |
|   // UI subpass
 | |
| 
 | |
|   // Draw UI colored rects ////////////////////////////////
 | |
|   vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_rect.pipeline);
 | |
|   vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_rect.layout, 0, 1, &ui_context->ui_descriptor_set, 0, NULL);
 | |
|   vkCmdBindVertexBuffers(command_buffer, 0, 1, &ui_context->ui_rect.vertex, &offset);
 | |
|   vkCmdBindIndexBuffer(command_buffer, ui_context->ui_rect.index, 0, VK_INDEX_TYPE_UINT32);
 | |
| 
 | |
|   for(uint32_t i = 0; i < ui_layer_count; i++) {
 | |
|     if(ui_layers[i].colored_rect_count > 0) {
 | |
|       vkCmdBindVertexBuffers(command_buffer, 1, 1, &ui_layers[i].colored_rects, &offset);
 | |
|       vkCmdDrawIndexed(command_buffer, 6, ui_layers[i].colored_rect_count, 0, 0, 0);
 | |
|     }
 | |
|   }
 | |
|   /////////////////////////////////////////////////////////
 | |
|   // Draw UI text /////////////////////////////////////////
 | |
|   vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, ui_context->ui_compute_text.pipeline);
 | |
|   vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_text.pipeline);
 | |
|   vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_text.layout, 0, 1, &ui_context->ui_descriptor_set, 0, NULL);
 | |
|   for(uint32_t i = 0; i < ui_layer_count; i++) {
 | |
|     if(ui_layers[i].text_count > 0) {
 | |
|       // Bind Font Descriptor
 | |
|       vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_text.layout, 1, 1, &ui_layers[i].font.set, 0, NULL);
 | |
|       // Push pointers
 | |
|       vkCmdPushConstants(command_buffer, ui_context->ui_pipeline_text.layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT, 0, 8, &ui_layers[i].texts_address);
 | |
|       vkCmdDrawIndexed(command_buffer, 6, ui_layers[i].text_count, 0, 0, 0);
 | |
|     }
 | |
|   }
 | |
|   /////////////////////////////////////////////////////////
 | |
| 
 | |
|   vkCmdEndRenderPass(command_buffer);
 | |
| 
 | |
|   result = vkEndCommandBuffer(command_buffer);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
|   
 | |
|   VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
 | |
|   VkSubmitInfo submit_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
 | |
|     .waitSemaphoreCount = 1,
 | |
|     .pWaitSemaphores = &context->image_available_semaphores[context->current_frame],
 | |
|     .pWaitDstStageMask = wait_stages,
 | |
|     .commandBufferCount = 1,
 | |
|     .pCommandBuffers = &command_buffer,
 | |
|     .signalSemaphoreCount = 1,
 | |
|     .pSignalSemaphores = &context->render_finished_semaphores[context->current_frame],
 | |
|   };
 | |
| 
 | |
|   result = vkQueueSubmit(context->graphics_queue.handle, 1, &submit_info, context->in_flight_fences[context->current_frame]);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   VkPresentInfoKHR present_info = {
 | |
|     .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
 | |
|     .waitSemaphoreCount = 1,
 | |
|     .pWaitSemaphores = &context->render_finished_semaphores[context->current_frame],
 | |
|     .swapchainCount = 1,
 | |
|     .pSwapchains = &context->swapchain,
 | |
|     .pImageIndices = &image_index,
 | |
|     .pResults = 0,
 | |
|   };
 | |
| 
 | |
|   result = vkQueuePresentKHR(context->present_queue.handle, &present_info);
 | |
|   if(result != VK_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   context->current_frame = (context->current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
 | |
| 
 | |
|   return VK_SUCCESS;
 | |
| }
 |