Added more setup(up to swapchain), and added dev/run to Makefile

main
noah metz 2024-01-07 16:34:09 -07:00
parent b97b377397
commit 9670362524
2 changed files with 528 additions and 22 deletions

@ -1,6 +1,7 @@
CFLAGS = -I/usr/local/include -O2 -Wall -Wextra CFLAGS = -I/usr/local/include -O0 -g -Wall -Wextra
LDFLAGS = -L/usr/local/lib -lglfw -lvulkan -ldl -Xlinker -rpath -Xlinker /usr/local/lib LDFLAGS = -L/usr/local/lib -lglfw -lvulkan -ldl -Xlinker -rpath -Xlinker /usr/local/lib
CC = gcc CC = clang
GDB = lldb
SOURCES = $(wildcard src/*.c) SOURCES = $(wildcard src/*.c)
@ -39,3 +40,9 @@ clean:
clean_compdb: clean_compdb:
rm -rf .compdb rm -rf .compdb
rm compile_commands.json rm compile_commands.json
run: spacegame
./spacegame
debug: spacegame
$(GDB) spacegame

@ -1,12 +1,72 @@
#define VK_USE_PLATFORM_MACOS_MVK
#include "vulkan/vulkan_core.h" #include "vulkan/vulkan_core.h"
#define GLFW_INCLUDE_VULKAN #define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#define GLFW_EXPOSE_NATIVE_COCOA
#include <GLFW/glfw3native.h>
#define GLM_FORCE_RADIANS #define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE #define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <cglm/mat4.h> #include <cglm/mat4.h>
#include <cglm/vec4.h> #include <cglm/vec4.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
typedef struct QueueIndicesStruct {
uint32_t graphics_family;
uint32_t graphics_index;
uint32_t present_family;
uint32_t present_index;
} QueueIndices;
typedef struct QueuesStruct {
VkQueue graphics;
VkQueue present;
} Queues;
typedef struct SwapchainDetailsStruct {
VkSurfaceCapabilitiesKHR capabilities;
VkSurfaceFormatKHR* formats;
uint32_t formats_count;
VkPresentModeKHR* present_modes;
uint32_t present_modes_count;
} SwapchainDetails;
typedef struct VulkanContextStruct {
VkInstance instance;
VkDebugUtilsMessengerEXT debug_messenger;
VkPhysicalDevice physical_device;
QueueIndices queue_indices;
VkDevice device;
Queues queues;
VkSurfaceKHR surface;
SwapchainDetails swapchain_details;
VkSwapchainKHR swapchain;
} VulkanContext;
const char * validation_layers[] = {
"VK_LAYER_KHRONOS_validation",
};
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_MVK_MACOS_SURFACE_EXTENSION_NAME,
VK_KHR_SURFACE_EXTENSION_NAME,
};
uint32_t instance_extension_count = sizeof(instance_extensions) / sizeof(const char *);
const char * device_extensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
uint32_t device_extension_count = sizeof(device_extensions) / sizeof(const char *);
void glfw_error(int error, const char* description) { void glfw_error(int error, const char* description) {
fprintf(stderr, "GLFW_ERR: 0x%02x - %s\n", error, description); fprintf(stderr, "GLFW_ERR: 0x%02x - %s\n", error, description);
@ -24,10 +84,193 @@ GLFWwindow* init_window(int width, int height) {
return window; 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);
return false;
}
}
free(available_layers);
return true;
};
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;
};
VkSurfaceKHR create_surface_khr(VkInstance instance, GLFWwindow* window) {
VkSurfaceKHR surface;
VkResult result = glfwCreateWindowSurface(instance, window, 0, &surface);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
return surface;
}
VkPhysicalDevice get_best_physical_device(VkInstance instance) {
VkPhysicalDevice device = VK_NULL_HANDLE;
uint32_t device_count = 0;
VkResult result;
result = vkEnumeratePhysicalDevices(instance, &device_count, 0);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
VkPhysicalDevice* devices = malloc(sizeof(VkPhysicalDevice)*device_count);
result = vkEnumeratePhysicalDevices(instance, &device_count, devices);
if(result != VK_SUCCESS) {
free(devices);
return VK_NULL_HANDLE;
}
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 device;
}
struct MaybeQueueIndices {
bool valid;
QueueIndices indices;
};
struct MaybeQueueIndices get_queue_indices(VkPhysicalDevice physical_device, VkSurfaceKHR surface) {
struct MaybeQueueIndices ret = {};
ret.indices.graphics_family = 0xFFFFFFFF;
ret.indices.graphics_index = 0xFFFFFFFF;
ret.indices.present_family = 0xFFFFFFFF;
ret.indices.present_index = 0xFFFFFFFF;
uint32_t queue_family_count;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, 0);
VkQueueFamilyProperties* queue_families = malloc(sizeof(VkQueueFamilyProperties)*queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families);
for(uint32_t i = 0;
(i < queue_family_count)
&& ((ret.indices.graphics_family == 0xFFFFFFFF) || (ret.indices.present_family == 0xFFFFFFFF));
i++) {
if(queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
ret.indices.graphics_family = i;
ret.indices.graphics_index = 0;
}
VkBool32 present_support = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface, &present_support);
if(present_support == VK_TRUE) {
ret.indices.present_family = i;
ret.indices.present_index = 0;
}
}
if((ret.indices.graphics_family != 0xFFFFFFFF) && (ret.indices.present_family != 0xFFFFFFFF)) {
ret.valid = true;
}
free(queue_families);
return ret;
}
VkDebugUtilsMessengerEXT create_debug_messenger(VkInstance instance) {
VkDebugUtilsMessengerEXT debug_messenger;
VkDebugUtilsMessengerCreateInfoEXT messenger_info = {};
messenger_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
messenger_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
messenger_info.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;
messenger_info.pfnUserCallback = debug_callback;
messenger_info.pUserData = 0;
PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
VkResult result;
result = func(instance, &messenger_info, 0, &debug_messenger);
if(result != VK_SUCCESS) {
fprintf(stderr, "failed to create debug messenger\n");
return VK_NULL_HANDLE;
}
return debug_messenger;
}
VkInstance create_instance() { VkInstance create_instance() {
VkInstance instance; VkInstance instance;
VkApplicationInfo app_info; if(check_validation_layers(validation_layers, validation_layer_count) == false) {
fprintf(stderr, "requested validation layers not supported");
return VK_NULL_HANDLE;
}
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.pApplicationName = "spacegame"; app_info.pApplicationName = "spacegame";
app_info.applicationVersion = VK_MAKE_VERSION(0, 0, 1); app_info.applicationVersion = VK_MAKE_VERSION(0, 0, 1);
@ -35,33 +278,36 @@ VkInstance create_instance() {
app_info.engineVersion = VK_MAKE_VERSION(0, 0, 1); app_info.engineVersion = VK_MAKE_VERSION(0, 0, 1);
app_info.apiVersion = VK_API_VERSION_1_3; app_info.apiVersion = VK_API_VERSION_1_3;
VkInstanceCreateInfo instance_info; VkInstanceCreateInfo instance_info = {};
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_info.pApplicationInfo = &app_info; instance_info.pApplicationInfo = &app_info;
instance_info.enabledLayerCount = validation_layer_count;
instance_info.ppEnabledLayerNames = validation_layers;
uint32_t glfwExtensionCount = 0; uint32_t glfwExtensionCount = 0;
const char** glfwExtensions; const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
const char** requested_extensions = malloc(sizeof(char*)*(glfwExtensionCount+1)); const char** requested_extensions = malloc(sizeof(char*)*(glfwExtensionCount + instance_extension_count));
for (uint32_t i = 0; i < glfwExtensionCount; i++) { for (uint32_t i = 0; i < glfwExtensionCount; i++) {
requested_extensions[i] = glfwExtensions[i]; requested_extensions[i] = glfwExtensions[i];
} }
requested_extensions[glfwExtensionCount] = VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME; for (uint32_t i = 0; i < instance_extension_count; i++) {
requested_extensions[glfwExtensionCount + i] = instance_extensions[i];
}
instance_info.enabledExtensionCount = glfwExtensionCount + 1; instance_info.enabledExtensionCount = glfwExtensionCount + instance_extension_count;
instance_info.ppEnabledExtensionNames = requested_extensions; instance_info.ppEnabledExtensionNames = requested_extensions;
instance_info.enabledLayerCount = 0;
instance_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; instance_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
VkResult result = vkCreateInstance(&instance_info, 0, &instance); VkResult result = vkCreateInstance(&instance_info, 0, &instance);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
fprintf(stderr, "vkCreateInstance: 0x%02x\n", result); fprintf(stderr, "vkCreateInstance: 0x%02x\n", result);
return 0; return VK_NULL_HANDLE;
} }
free(requested_extensions); free(requested_extensions);
@ -69,14 +315,242 @@ VkInstance create_instance() {
return instance; return instance;
} }
int init_vulkan() { VkDevice create_logical_device(VkPhysicalDevice physical_device, QueueIndices queue_indices) {
VkDevice device;
VkDeviceQueueCreateInfo queue_create_info[2] = {};
uint32_t queue_create_count;
float default_queue_priority = 1.0f;
if(queue_indices.graphics_family == queue_indices.present_family) {
queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info[0].queueFamilyIndex = queue_indices.graphics_family;
queue_create_info[0].queueCount = 1;
queue_create_info[0].pQueuePriorities = &default_queue_priority;
queue_create_count = 1;
} else {
queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info[0].queueFamilyIndex = queue_indices.graphics_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 = queue_indices.present_family;
queue_create_info[1].queueCount = 1;
queue_create_info[1].pQueuePriorities = &default_queue_priority;
queue_create_count = 2;
}
VkPhysicalDeviceFeatures device_features = {};
VkDeviceCreateInfo device_create_info = {};
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_create_info.pQueueCreateInfos = queue_create_info;
device_create_info.queueCreateInfoCount = queue_create_count;
device_create_info.pEnabledFeatures = &device_features;
device_create_info.enabledExtensionCount = device_extension_count;
device_create_info.ppEnabledExtensionNames = device_extensions;
device_create_info.enabledLayerCount = validation_layer_count;
device_create_info.ppEnabledLayerNames = validation_layers;
VkResult result = vkCreateDevice(physical_device, &device_create_info, 0, &device);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
return device;
}
struct MaybeSwapchainDetails {
bool valid;
SwapchainDetails details;
};
struct MaybeSwapchainDetails get_swapchain_details(VkPhysicalDevice physical_device, VkSurfaceKHR surface) {
struct MaybeSwapchainDetails ret = {};
ret.valid = false;
ret.details.formats = 0;
ret.details.present_modes = 0;
VkResult result;
result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &ret.details.capabilities);
if(result != VK_SUCCESS) {
return ret;
}
result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &ret.details.formats_count, 0);
if(result != VK_SUCCESS) {
return ret;
}
ret.details.formats = malloc(sizeof(VkSurfaceFormatKHR)*ret.details.formats_count);
result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &ret.details.formats_count, ret.details.formats);
if(result != VK_SUCCESS) {
free(ret.details.formats);
return ret;
}
result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &ret.details.present_modes_count, 0);
if(result != VK_SUCCESS) {
free(ret.details.formats);
return ret;
}
ret.details.present_modes = malloc(sizeof(VkPresentModeKHR)*ret.details.present_modes_count);
result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &ret.details.present_modes_count, ret.details.present_modes);
if(result != VK_SUCCESS) {
free(ret.details.formats);
free(ret.details.present_modes);
return ret;
}
ret.valid = true;
return ret;
}
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;
}
VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_details, VkSurfaceKHR surface, QueueIndices indices, VkSwapchainKHR old_swapchain) {
VkSurfaceFormatKHR format = choose_swapchain_format(swapchain_details);
VkPresentModeKHR present_mode = choose_present_mode(swapchain_details);
VkExtent2D extent = choose_swapchain_extent(swapchain_details);
uint32_t image_count = swapchain_details.capabilities.minImageCount + 1;
uint32_t max_images = swapchain_details.capabilities.maxImageCount;
if((max_images > 0) && (image_count > max_images)) {
image_count = max_images;
}
VkSwapchainCreateInfoKHR swapchain_info = {};
swapchain_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchain_info.surface = surface;
swapchain_info.minImageCount = image_count;
swapchain_info.imageFormat = format.format;
swapchain_info.imageColorSpace = format.colorSpace;
swapchain_info.imageExtent = extent;
swapchain_info.imageArrayLayers = 1;
swapchain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
uint32_t queue_families[2] = {indices.graphics_family, indices.present_index};
if(indices.graphics_family != indices.present_family) {
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;
}
swapchain_info.preTransform = swapchain_details.capabilities.currentTransform;
swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
swapchain_info.presentMode = present_mode;
swapchain_info.clipped = VK_TRUE;
swapchain_info.oldSwapchain = old_swapchain;
VkSwapchainKHR swapchain;
VkResult result;
result = vkCreateSwapchainKHR(device, &swapchain_info, 0, &swapchain);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
return swapchain;
}
VulkanContext* init_vulkan(GLFWwindow* window) {
VulkanContext* context = (VulkanContext*)malloc(sizeof(VulkanContext));
VkInstance instance = create_instance(); VkInstance instance = create_instance();
if(instance == 0) { if(instance == VK_NULL_HANDLE) {
return 1; fprintf(stderr, "failed to initialize vulkan instance\n");
return 0;
} else {
context->instance = instance;
}
VkDebugUtilsMessengerEXT debug_messenger = create_debug_messenger(context->instance);
if(debug_messenger == VK_NULL_HANDLE) {
fprintf(stderr, "failed to initialize vulkan debug messenger\n");
return 0;
} else {
context->debug_messenger = debug_messenger;
}
VkPhysicalDevice physical_device = get_best_physical_device(context->instance);
if(physical_device == VK_NULL_HANDLE) {
fprintf(stderr, "failed to pick vulkan physical device\n");
return 0;
} else {
context->physical_device = physical_device;
}
VkSurfaceKHR surface = create_surface_khr(context->instance, window);
if(surface == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan surface\n");
return 0;
} else {
context->surface = surface;
}
struct MaybeQueueIndices maybe_indices = get_queue_indices(context->physical_device, context->surface);
if(maybe_indices.valid == false) {
fprintf(stderr, "failed to get vulkan queue indices\n");
return 0;
} else {
context->queue_indices = maybe_indices.indices;
} }
vkDestroyInstance(instance, 0); VkDevice device = create_logical_device(context->physical_device, context->queue_indices);
if(device == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan logical device\n");
return 0; return 0;
} else {
context->device = device;
}
vkGetDeviceQueue(device, context->queue_indices.graphics_family, context->queue_indices.graphics_index, &context->queues.graphics);
vkGetDeviceQueue(device, context->queue_indices.present_family, context->queue_indices.present_index, &context->queues.present);
struct MaybeSwapchainDetails maybe_details = get_swapchain_details(context->physical_device, context->surface);
if(maybe_details.valid == false) {
fprintf(stderr, "failed to create vulkan logical device\n");
return 0;
} else {
context->swapchain_details = maybe_details.details;
}
VkSwapchainKHR swapchain = create_swapchain(context->device, context->swapchain_details, context->surface, context->queue_indices, VK_NULL_HANDLE);
if(swapchain == VK_NULL_HANDLE) {
return 0;
} else {
context->swapchain = swapchain;
}
return context;
} }
void main_loop(GLFWwindow* window) { void main_loop(GLFWwindow* window) {
@ -85,10 +559,35 @@ void main_loop(GLFWwindow* window) {
} }
} }
void cleanup(GLFWwindow* window) { void cleanup(GLFWwindow* window, VulkanContext* context) {
glfwDestroyWindow(window); if(context != 0) {
if(context->instance != VK_NULL_HANDLE) {
if(context->swapchain != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(context->device, context->swapchain, 0);
}
if(context->surface != VK_NULL_HANDLE) {
vkDestroySurfaceKHR(context->instance, context->surface, 0);
}
if(context->device != VK_NULL_HANDLE) {
vkDestroyDevice(context->device, 0);
}
if(context->debug_messenger != VK_NULL_HANDLE) {
PFN_vkDestroyDebugUtilsMessengerEXT destroy_messenger = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context->instance, "vkDestroyDebugUtilsMessengerEXT");
destroy_messenger(context->instance, context->debug_messenger, 0);
}
vkDestroyInstance(context->instance, 0);
}
free(context);
}
if(window != 0) {
glfwDestroyWindow(window);
glfwTerminate(); glfwTerminate();
}
} }
int main() { int main() {
@ -98,15 +597,15 @@ int main() {
return 1; return 1;
} }
int result = init_vulkan(); VulkanContext* context = init_vulkan(window);
if (result != 0) { if (context == 0) {
fprintf(stderr, "failed to initialize vulkan: err %d\n", result); fprintf(stderr, "failed to initialize vulkan context\n");
return 2; return 2;
} }
main_loop(window); main_loop(window);
cleanup(window); cleanup(window, context);
return 0; return 0;
} }