#define VK_USE_PLATFORM_MACOS_MVK #include "vulkan/vulkan_core.h" #include "vulkan/vk_enum_string_helper.h" #define GLFW_INCLUDE_VULKAN #include #define GLFW_EXPOSE_NATIVE_COCOA #include #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE #include #include #include #include #include #include #include #include #include #include #include #include typedef struct QueueStruct { VkQueue handle; uint32_t family; uint32_t index; } Queue; typedef struct SwapchainDetailsStruct { VkSurfaceCapabilitiesKHR capabilities; VkSurfaceFormatKHR* formats; uint32_t formats_count; VkPresentModeKHR* present_modes; uint32_t present_modes_count; } SwapchainDetails; typedef struct SwapchainImagesStruct { VkImage* images; uint32_t count; } SwapchainImages; typedef struct TextureStruct { GPUImage image; VkImageView view; VkSampler sampler; } Texture; typedef struct PositionStruct { vec3 position; vec3 scale; versor rotation; } Position; typedef void(*MappingFunc)(void*,void*); void attribute_mapping_position_to_matrix(void* dest, void* source) { Position* position = source; glm_translate_make(dest, position->position); glm_quat_rotate(dest, position->rotation, dest); glm_scale(dest, position->scale); } #define MAPPING_POSITION_TO_MATRIX 0 MappingFunc mapping_functions[] = { attribute_mapping_position_to_matrix, }; typedef struct MappingStruct { uint32_t mapping_type; // What function to use to map it uint32_t index; // Which index to use in the ATTRIBUTE_ID_DESCRIPTORS array } Mapping; #define ATTRIBUTE_ID_MESH 0x00000001 // Mesh* #define ATTRIBUTE_ID_PIPELINE 0x00000002 // GraphicsPipeline* #define ATTRIBUTE_ID_DESCRIPTORS 0x00000003 // void***(array of array of data pointers) #define ATTRIBUTE_ID_DESCRIPTOR_SETS 0x00000004 // VkDescriptorSet* #define ATTRIBUTE_ID_POSITION 0x00000005 // Position* #define ATTRIBUTE_ID_PUSH_CONSTANTS 0x00000006 // void* typedef struct ObjectStruct { Map attributes; } Object; typedef struct MeshStruct { uint32_t vertex_count; GPUBuffer vertex_buffer; uint32_t index_count; GPUBuffer index_buffer; } Mesh; typedef struct GraphicsPipelineInfoStruct { VkDescriptorSetLayout scene_layout; VkDescriptorSetLayout set_layout; VkPipelineVertexInputStateCreateInfo input_info; uint32_t shader_stages_count; VkPipelineShaderStageCreateInfo* shader_stages; } GraphicsPipelineInfo; typedef struct GraphicsPipelineStruct { uint32_t max_frames_in_flight; uint32_t max_objects; VkDescriptorPool descriptor_pool; VkDescriptorSet* descriptors; VkPipelineLayout layout; VkPipeline pipeline; VkPipeline offscreen_pipeline; } GraphicsPipeline; typedef struct VulkanContextStruct { VkInstance instance; VkDebugUtilsMessengerEXT debug_messenger; VkPhysicalDevice physical_device; VkDevice device; Queue graphics_queue; Queue transfer_queue; Queue present_queue; // G Buffer GPUPage* g_buffer_page; VkRenderPass g_renderpass; VkFramebuffer g_framebuffer; GPUImage g_image_position; GPUImage g_image_normal; GPUImage g_image_depth; VkImageView g_image_view_position; VkImageView g_image_view_normal; VkImageView g_image_view_depth; VkFormat g_image_format_position; VkFormat g_image_format_normal; VkFormat g_image_format_depth; // Present Swapchain VkSurfaceKHR surface; SwapchainDetails swapchain_details; VkSwapchainKHR swapchain; VkSurfaceFormatKHR swapchain_format; VkPresentModeKHR swapchain_present_mode; VkExtent2D swapchain_extent; uint32_t swapchain_image_count; // Per image objects VkImage* swapchain_images; VkImageView* swapchain_image_views; VkFramebuffer* swapchain_framebuffers; VkDeviceMemory depth_image_memory; VkImage depth_image; VkFormat depth_format; VkImageView depth_image_view; uint32_t max_frames_in_flight; // Per frame objects VkCommandBuffer* offscreen_command_buffers; VkCommandBuffer* swapchain_command_buffers; VkSemaphore* image_available_semaphores; VkSemaphore* render_finished_semaphores; VkSemaphore* offscreen_complete_semaphores; VkFence* in_flight_fences; VkRenderPass render_pass; VkCommandPool graphics_command_pool; VkCommandPool transfer_command_pool; uint32_t current_frame; VkPhysicalDeviceMemoryProperties memories; VkCommandPool extra_graphics_pool; } VulkanContext; typedef struct SceneContextStruct { VkDescriptorPool pool; VkDescriptorSetLayout descriptor_layout; VkDescriptorSet* descriptors; GPUBuffer* ubos; void** ubo_ptrs; uint32_t pcr_size; } SceneContext; struct TextureVertex { vec3 pos; vec3 color; vec2 tex; }; struct Vertex { vec3 pos; vec3 color; }; struct SceneUBO { mat4 test; }; struct ScenePC { mat4 view; mat4 proj; }; const struct Vertex vertices[] = { {.pos = {-1.f, -1.f, 0.f}, .color = {1.0f, 0.0f, 0.0f}}, {.pos = { 1.f, -1.f, 0.f}, .color = {0.0f, 1.0f, 0.0f}}, {.pos = { 1.f, 1.f, 0.f}, .color = {0.0f, 0.0f, 1.0f}}, {.pos = {-1.f, 1.f, 0.f}, .color = {1.0f, 1.0f, 1.0f}}, }; const struct TextureVertex texture_vertices[] = { {.pos = {-1.f, -1.f, 0.f}, .color = {1.0f, 0.0f, 0.0f}, .tex = {1.0f, 1.0f}}, {.pos = { 1.f, -1.f, 0.f}, .color = {0.0f, 1.0f, 0.0f}, .tex = {0.0f, 1.0f}}, {.pos = { 1.f, 1.f, 0.f}, .color = {0.0f, 0.0f, 1.0f}, .tex = {0.0f, 0.0f}}, {.pos = {-1.f, 1.f, 0.f}, .color = {1.0f, 1.0f, 1.0f}, .tex = {1.0f, 0.0f}}, }; const uint16_t indices[] = { 2, 1, 0, 0, 3, 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_metal_surface", 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, VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, "VK_KHR_portability_subset", }; uint32_t device_extension_count = sizeof(device_extensions) / sizeof(const char *); void glfw_error(int error, const char* description) { fprintf(stderr, "GLFW_ERR: 0x%02x - %s\n", error, description); } GLFWwindow* init_window(int width, int height) { glfwInit(); glfwSetErrorCallback(glfw_error); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); GLFWwindow* window = glfwCreateWindow(width, height, "Vulkan window", 0, 0); return window; } 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; } VkFormat find_depth_format(VkPhysicalDevice physical_device, uint32_t num_requested, VkFormat* requested, VkImageTiling tiling, VkFormatFeatureFlags features) { for(uint32_t i = 0; i < num_requested; i++) { VkFormatProperties properties; vkGetPhysicalDeviceFormatProperties(physical_device, requested[i], &properties); if(tiling == VK_IMAGE_TILING_LINEAR && (properties.linearTilingFeatures & features) == features) { return requested[i]; } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (properties.optimalTilingFeatures & features) == features) { return requested[i]; } } return VK_FORMAT_MAX_ENUM; } 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; } VkDescriptorSet* create_descriptor_sets(VkDevice device, VkDescriptorSetLayout layout, VkDescriptorPool pool, uint32_t count) { VkDescriptorSetLayout* layouts = malloc(sizeof(VkDescriptorSetLayout)*count); if(layouts == 0) { return 0; } VkDescriptorSet* sets = malloc(sizeof(VkDescriptorSet)*count); if(sets == 0) { free(layouts); return 0; } for(uint32_t i = 0; i < count; i++) { layouts[i] = layout; } VkDescriptorSetAllocateInfo alloc_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorPool = pool, .descriptorSetCount = count, .pSetLayouts = layouts, }; VkResult result = vkAllocateDescriptorSets(device, &alloc_info, sets); free(layouts); if(result != VK_SUCCESS) { free(sets); return 0; } return sets; } 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); fprintf(stderr, "%d\n", properties.limits.maxPerStageResources); 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_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) { fprintf(stderr, "failed to create debug messenger\n"); return result; } return VK_SUCCESS; } VkResult create_instance(VkInstance* instance) { if(instance == NULL) { return VK_ERROR_VALIDATION_FAILED_EXT; } if(check_validation_layers(validation_layers, validation_layer_count) == false) { fprintf(stderr, "requested validation layers not supported"); return VK_ERROR_VALIDATION_FAILED_EXT; } VkApplicationInfo app_info = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "spacegame", .applicationVersion = VK_MAKE_VERSION(0, 0, 1), .pEngineName = "spacegame", .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) { fprintf(stderr, "vkCreateInstance: %s\n", string_VkResult(result)); return result; } free(requested_extensions); 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; } uint32_t family_indices[] = { transfer_queue->family, graphics_queue->family, present_queue->family, }; VkDeviceQueueCreateInfo queue_create_info[3] = {}; float default_queue_priority = 1.0f; for(uint32_t i = 0; i < 3; i++) { queue_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_create_info[i].queueFamilyIndex = family_indices[i]; queue_create_info[i].queueCount = 1; queue_create_info[i].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 = 3, .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 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; } VkSwapchainKHR 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 old_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 = old_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; } VkSwapchainKHR swapchain; VkResult result; result = vkCreateSwapchainKHR(device, &swapchain_info, 0, &swapchain); if(result != VK_SUCCESS) { return VK_NULL_HANDLE; } return swapchain; } SwapchainImages get_swapchain_images(VkDevice device, VkSwapchainKHR swapchain) { SwapchainImages images = { .images = NULL, .count = 0, }; VkResult result; result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, 0); if(result != VK_SUCCESS) { images.count = 0; return images; } images.images = malloc(sizeof(VkImage)*images.count); if(images.images == 0) { images.count = 0; return images; } result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, images.images); if(result != VK_SUCCESS) { images.count = 0; return images; } return images; } VkImageView* create_image_views(VkDevice device, uint32_t image_count, VkImage* images, VkSurfaceFormatKHR format) { VkImageView* image_views = malloc(sizeof(VkImageView)*image_count); if(image_views == 0) { return 0; } 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 0; } } return image_views; } VkFramebuffer* create_swapchain_framebuffers(VkDevice device, uint32_t image_count, VkImageView* image_views, VkImageView depth_image_view, VkRenderPass render_pass, VkExtent2D extent) { VkFramebuffer* framebuffers = malloc(sizeof(VkFramebuffer)*image_count); if(framebuffers == 0) { return 0; } 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, &framebuffers[i]); if(result != VK_SUCCESS) { free(framebuffers); return 0; } } return framebuffers; } VkShaderModule create_shader_module(VkDevice device, const char * code, uint32_t code_size) { VkShaderModuleCreateInfo shader_info = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = code_size, .pCode = (uint32_t*)code, }; VkShaderModule shader; VkResult result; result = vkCreateShaderModule(device, &shader_info, 0, &shader); if(result != VK_SUCCESS) { return VK_NULL_HANDLE; } return shader; } VkShaderModule load_shader_file(const char* path, VkDevice device) { FILE* file; file = fopen(path, "rb"); if(file == 0) { return VK_NULL_HANDLE; } int result = fseek(file, 0, SEEK_END); if(result != 0) { return VK_NULL_HANDLE; } long buffer_size = ftell(file); result = fseek(file, 0, SEEK_SET); if(result != 0) { return VK_NULL_HANDLE; } char * buffer = malloc(buffer_size); if(buffer == 0) { return VK_NULL_HANDLE; } size_t read = fread(buffer, 1, buffer_size, file); VkShaderModule shader = create_shader_module(device, buffer, read); free(buffer); return shader; } VkRenderPass create_render_pass(VkDevice device, VkSurfaceFormatKHR format, VkFormat depth_format) { 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, }, }; // 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, } }; 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, }; VkRenderPass render_pass; VkResult result = vkCreateRenderPass(device, &render_info, 0, &render_pass); if(result != VK_SUCCESS) { return VK_NULL_HANDLE; } return render_pass; } VkCommandBuffer command_begin_single(VkDevice device, VkCommandPool transfer_pool) { VkCommandBufferAllocateInfo command_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandPool = transfer_pool, .commandBufferCount = 1, }; VkCommandBuffer command_buffer; VkResult result = vkAllocateCommandBuffers(device, &command_info, &command_buffer); if(result != VK_SUCCESS) { return VK_NULL_HANDLE; } VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; result = vkBeginCommandBuffer(command_buffer, &begin_info); if(result != VK_SUCCESS) { vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); return VK_NULL_HANDLE; } return command_buffer; } VkResult command_end_single(VkDevice device, VkCommandBuffer command_buffer, VkCommandPool transfer_pool, Queue transfer_queue) { VkResult result = vkEndCommandBuffer(command_buffer); if(result != VK_SUCCESS) { vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); return result; } VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &command_buffer, }; result = vkQueueSubmit(transfer_queue.handle, 1, &submit_info, 0); if(result != VK_SUCCESS) { vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); return result; } result = vkQueueWaitIdle(transfer_queue.handle); vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); return result; } VkResult command_transition_image_layout(VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue, VkImageLayout old_layout, VkImageLayout new_layout, VkImage image, VkAccessFlags src_mask, VkAccessFlags dst_mask, VkPipelineStageFlags source, VkPipelineStageFlags dest, uint32_t source_family, uint32_t dest_family, VkImageAspectFlags aspect_flags) { VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool); VkImageMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = old_layout, .newLayout = new_layout, .srcQueueFamilyIndex = source_family, .dstQueueFamilyIndex = dest_family, .image = image, .subresourceRange = { .aspectMask = aspect_flags, .levelCount = 1, .layerCount = 1, .baseMipLevel = 0, .baseArrayLayer = 0, }, .srcAccessMask = src_mask, .dstAccessMask = dst_mask, }; vkCmdPipelineBarrier(command_buffer, source, dest, 0, 0, 0, 0, 0, 1, &barrier); return command_end_single(device, command_buffer, transfer_pool, transfer_queue); } VkResult command_copy_buffer_to_image(VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue, VkExtent3D image_size, VkBuffer source, VkImage dest) { VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool); VkBufferImageCopy region = { .bufferOffset = 0, .bufferRowLength = 0, .bufferImageHeight = 0, .imageSubresource = { .baseArrayLayer = 0, .layerCount = 1, .mipLevel = 0, .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, }, .imageOffset = { .x = 0, .y = 0, .z = 0, }, .imageExtent = image_size, }; vkCmdCopyBufferToImage(command_buffer, source, dest, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); return command_end_single(device, command_buffer, transfer_pool, transfer_queue); } Texture load_texture(VkDevice device, GPUPage* page, GPUBuffer staging, VkCommandPool transfer_pool, Queue transfer_queue, VkCommandPool graphics_pool, Queue graphics_queue, VkExtent2D size, VkFormat format, void* image_data){ Texture ret = { .image.page = NULL, .image.memory = NULL, .image.handle = VK_NULL_HANDLE, .view = VK_NULL_HANDLE, }; VkExtent3D full_extent = { .width = size.width, .height = size.height, .depth = 1, }; VkImageCreateInfo info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .extent = full_extent, .mipLevels = 1, .arrayLayers = 1, .format = format, .tiling = VK_IMAGE_TILING_OPTIMAL, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .samples = VK_SAMPLE_COUNT_1_BIT, .flags = 0, }; GPUImage image = {0}; VkResult result = gpu_image_malloc(device, page, &info, &image); if(result != VK_SUCCESS) { return ret; } memcpy(staging.page->ptr + staging.memory->offset, image_data, image.memory->size); result = command_transition_image_layout(device, transfer_pool, transfer_queue, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, image.handle, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, transfer_queue.family, transfer_queue.family, VK_IMAGE_ASPECT_COLOR_BIT); if(result != VK_SUCCESS) { gpu_image_free(device, image); return ret; } result = command_copy_buffer_to_image(device, transfer_pool, transfer_queue, full_extent, staging.handle, image.handle); if(result != VK_SUCCESS) { gpu_image_free(device, image); return ret; } result = command_transition_image_layout(device, transfer_pool, transfer_queue, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, image.handle, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, transfer_queue.family, graphics_queue.family, VK_IMAGE_ASPECT_COLOR_BIT); if(result != VK_SUCCESS) { gpu_image_free(device, image); return ret; } result = command_transition_image_layout(device, graphics_pool, graphics_queue, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, image.handle, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, transfer_queue.family, graphics_queue.family, VK_IMAGE_ASPECT_COLOR_BIT); if(result != VK_SUCCESS) { gpu_image_free(device, image); return ret; } VkImageView view; VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = image.handle, .viewType = VK_IMAGE_VIEW_TYPE_2D, .components = { .a = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY, .r = VK_COMPONENT_SWIZZLE_IDENTITY, }, .format = format, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .layerCount = 1, .levelCount = 1, .baseArrayLayer = 0, .baseMipLevel = 0, }, }; result = vkCreateImageView(device, &view_info, 0, &view); if(result != VK_SUCCESS) { gpu_image_free(device, image); return ret; } VkSampler sampler; VkSamplerCreateInfo sampler_info = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = VK_FILTER_NEAREST, .minFilter = VK_FILTER_NEAREST, .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, .anisotropyEnable = VK_FALSE, .maxAnisotropy = 2.0f, .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, .unnormalizedCoordinates = VK_FALSE, .compareEnable = VK_FALSE, .compareOp = VK_COMPARE_OP_ALWAYS, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .mipLodBias = 0.0f, .minLod = 0.0f, .maxLod = 0.0f, }; result = vkCreateSampler(device, &sampler_info, 0, &sampler); if(result != VK_SUCCESS) { gpu_image_free(device, image); vkDestroyImageView(device, view, 0); return ret; } ret.image = image; ret.view = view; ret.sampler = sampler; return ret; } int create_depth_image(VulkanContext* context) { VkExtent3D depth_extent = { .width = context->swapchain_extent.width, .height = context->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 = context->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, }; VkImage depth_image; VkResult result = vkCreateImage(context->device, &depth_image_info, 0, &depth_image); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create depth image\n"); return 1; } else { context->depth_image = depth_image; } VkMemoryRequirements depth_image_requirements; vkGetImageMemoryRequirements(context->device, context->depth_image, &depth_image_requirements); VkMemoryAllocateInfo depth_memory_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = depth_image_requirements.size, .memoryTypeIndex = pick_memory(context->memories, depth_image_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, 0).index, }; VkDeviceMemory depth_image_memory; result = vkAllocateMemory(context->device, &depth_memory_info, 0, &depth_image_memory); if(result != VK_SUCCESS) { fprintf(stderr, "failed to allocate memory for depth image\n"); return 2; } else { context->depth_image_memory = depth_image_memory; } result = vkBindImageMemory(context->device, context->depth_image, context->depth_image_memory, 0); if(result != VK_SUCCESS) { fprintf(stderr, "failed to bind memory for depth image\n"); return 3; } VkImageViewCreateInfo depth_view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = context->depth_image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = context->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, }, }; VkImageView depth_image_view; result = vkCreateImageView(context->device, &depth_view_info, 0, &depth_image_view); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create vulkan depth image view\n"); return 4; } else { context->depth_image_view = depth_image_view; } result = command_transition_image_layout(context->device, context->extra_graphics_pool, context->graphics_queue, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, context->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) { fprintf(stderr, "failed to transition depth image\n"); return 5; } return 0; } VkResult recreate_swapchain(VulkanContext* context) { for(uint32_t i = 0; i < context->swapchain_image_count; i++) { vkDestroyFramebuffer(context->device, context->swapchain_framebuffers[i], 0); vkDestroyImageView(context->device, context->swapchain_image_views[i], 0); } for(uint32_t i = 0; i < context->max_frames_in_flight; i++) { vkDestroySemaphore(context->device, context->image_available_semaphores[i], 0); } free(context->swapchain_image_views); free(context->swapchain_framebuffers); free(context->swapchain_details.formats); free(context->swapchain_details.present_modes); vkDestroyImageView(context->device, context->depth_image_view, 0); vkDestroyImage(context->device, context->depth_image, 0); vkFreeMemory(context->device, context->depth_image_memory, 0); VkResult 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); create_depth_image(context); VkSwapchainKHR swapchain = 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(swapchain == VK_NULL_HANDLE) { context->swapchain = VK_NULL_HANDLE; return VK_ERROR_INITIALIZATION_FAILED; } else { context->swapchain = swapchain; } SwapchainImages swapchain_images = get_swapchain_images(context->device, context->swapchain); if(swapchain_images.count == 0) { return VK_ERROR_INITIALIZATION_FAILED; } else { context->swapchain_images = swapchain_images.images; context->swapchain_image_count = swapchain_images.count; } VkImageView* image_views = create_image_views(context->device, context->swapchain_image_count, context->swapchain_images, context->swapchain_format); if(image_views == 0) { return VK_ERROR_INITIALIZATION_FAILED; } else { context->swapchain_image_views = image_views; } VkFramebuffer* framebuffers = create_swapchain_framebuffers(context->device, context->swapchain_image_count, context->swapchain_image_views, context->depth_image_view, context->render_pass, context->swapchain_extent); if(framebuffers == 0) { return VK_ERROR_INITIALIZATION_FAILED; } else { context->swapchain_framebuffers = framebuffers; } VkSemaphore* ia_semaphores = create_semaphores(context->device, 0, context->max_frames_in_flight); if(ia_semaphores == 0) { fprintf(stderr, "failed to create vulkan image available semaphores\n"); return 0; } else { context->image_available_semaphores = ia_semaphores; } return VK_SUCCESS; } void command_draw_object(Object object, VkCommandBuffer command_buffer) { MaybeValue maybe_mesh = map_lookup(object.attributes, ATTRIBUTE_ID_MESH); if(maybe_mesh.has_value == false) { return; } Mesh* mesh = maybe_mesh.value; VkBuffer vertex_buffers[] = {mesh->vertex_buffer.handle}; VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, offsets); vkCmdBindIndexBuffer(command_buffer, mesh->index_buffer.handle, 0, VK_INDEX_TYPE_UINT16); vkCmdDrawIndexed(command_buffer, mesh->index_count, 1, 0, 0, 0); } void command_draw_pipeline(GraphicsPipeline pipeline, uint32_t object_count, Object* objects, uint32_t frame_num, VkCommandBuffer command_buffer, int offscreen) { if(offscreen) { vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.offscreen_pipeline); } else { vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline); } if(pipeline.descriptors != NULL) { vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 1, 1, &pipeline.descriptors[frame_num], 0, 0); } for(uint32_t i = 0; i < object_count; i++) { command_draw_object(objects[i], command_buffer); } } // Setup a scene for vkCmdDrawIndexedIndirect // Before the call is made the following needs to be ready: // 1) Vertex data for entire scene in one vertex buffer // 2) Index data for entire scene in one index buffer // 3) Descriptor data in single descriptor set // a) e.x. all textures for the draw call need to be in the bound pipeline descriptor set // 4) Object Data populated per-instance into buffer which will be pushed by VkDeviceAddress // 5) Buffer of draw command ordered by instance id(first is 0, increase by 1 every 'stride') // // Once that is setup, the call to vkCmdDrawIndexed will happen and then: // 1) For each draw command read from the command buffer // a) Consume the indices/vertices to run the vertex shaders // i) Vertex shaders access the per-instance object data indexed by gl_instanceID // ii) This object data can have indices for other descriptors // x) e.x. a shader can read object data with int Z that tell it to use a sampler at binding Z // // If I want to start by not worrying about dynamic object load/unload then I can create the above buffers // when loading the scene, and free them all at once // // If I want to start worrying about loading/unloading then I need to find a way to efficiently reconstruct // these buffers on changes // Ideas: // - Mark objects as 'deleted' to cheaply cull during a compute cull/vertex shader // - Would require only a 1-bit change per deletion // - Would require defragmentation periodically(or whenever trying to add without space) VkResult create_scene(){ return VK_SUCCESS; } VkResult command_draw_scene(uint32_t pipelines_count, GraphicsPipeline* pipelines, uint32_t* object_counts, Object** objects, uint32_t frame_num, VkDescriptorSet* scene_descriptors, struct ScenePC* scene_constants, VkCommandBuffer command_buffer, VkRenderPass render_pass, VkFramebuffer framebuffer, VkExtent2D extent, VkDeviceAddress object_buffer_addr, int offscreen) { VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = 0, .pInheritanceInfo = 0, }; VkResult result = vkBeginCommandBuffer(command_buffer, &begin_info); if(result != VK_SUCCESS) { return result; } VkClearValue clear_colors[] = { { .color = { {0.0f, 0.0f, 0.0f, 1.0f} } }, { .depthStencil = {1.0f, 0.0f}, }, }; VkRenderPassBeginInfo render_pass_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = render_pass, .framebuffer = framebuffer, .renderArea = { .offset = { .x = 0, .y = 0, }, .extent = extent, }, .clearValueCount = sizeof(clear_colors)/sizeof(VkClearValue), .pClearValues = clear_colors, }; vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport = { .x = 0.0f, .y = 0.0f, .width = (float)(extent.width), .height = (float)(extent.height), .minDepth = 0.0f, .maxDepth = 1.0f, }; vkCmdSetViewport(command_buffer, 0, 1, &viewport); VkRect2D scissor = { .offset = { .x = 0.0f, .y = 0.0f, }, .extent = extent, }; vkCmdSetScissor(command_buffer, 0, 1, &scissor); vkCmdPushConstants(command_buffer, pipelines[0].layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(struct ScenePC), scene_constants); vkCmdPushConstants(command_buffer, pipelines[0].layout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(struct ScenePC), sizeof(VkDeviceAddress), &object_buffer_addr); // Bind the scene descriptor vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[0].layout, 0, 1, &scene_descriptors[frame_num], 0, 0); for(uint i = 0; i < pipelines_count; i++) { command_draw_pipeline(pipelines[i], object_counts[i], objects[i], frame_num, command_buffer, offscreen); } vkCmdEndRenderPass(command_buffer); return vkEndCommandBuffer(command_buffer); } 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; } 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; } Object create_object() { Object ret = { .attributes = { .buckets = 0, }, }; Map attributes = map_create(8, 2); if(attributes.buckets == 0) { return ret; } ret.attributes = attributes; return ret; } Object create_renderable(Mesh* mesh, GraphicsPipeline* pipeline) { Object zero = { .attributes = { .buckets = 0, }, }; Object object = create_object(); if(object.attributes.buckets == 0) { return zero; } if(mesh == 0 || pipeline == 0) { return zero; } bool result = map_add(&object.attributes, ATTRIBUTE_ID_MESH, mesh); if(result == false) { map_destroy(object.attributes); return zero; } result = map_add(&object.attributes, ATTRIBUTE_ID_PIPELINE, pipeline); if(result == false) { map_destroy(object.attributes); return zero; } return object; } typedef struct TextureSetStruct { uint32_t max_images; Texture* textures; VkDescriptorSet descriptor; VkDescriptorPool pool; } TextureSet; // TODO /*VkResult texture_set_add(VkDevice device, GPUPage* page, GPUBuffer staging, VkCommandPool transfer_pool, VkQueue transfer_queue, VkCommandPool graphics_pool, VkQueue graphics_queue, VkExtent2D size, VkFormat format, void* image_data, uint32_t transfer_family, uint32_t graphics_family) { return VK_SUCCESS; }*/ VkResult create_texture_set(VkDevice device, VkDescriptorSetLayout layout, uint32_t max_images, TextureSet* out) { if(out == NULL) { return VK_ERROR_VALIDATION_FAILED_EXT; } out->max_images = max_images; out->textures = malloc(sizeof(Texture*)*max_images); if(out->textures == NULL) { return VK_ERROR_OUT_OF_HOST_MEMORY; } VkDescriptorPoolSize sizes[] = { { .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = max_images, }, }; VkDescriptorPoolCreateInfo pool_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, .maxSets = 1, .pPoolSizes = sizes, .poolSizeCount = sizeof(sizes) / sizeof(VkDescriptorPoolSize), .pNext = NULL, }; VkResult result = vkCreateDescriptorPool(device, &pool_info, 0, &out->pool); if(result != VK_SUCCESS) { free(out->textures); return result; }; VkDescriptorSetLayout layouts[] = { layout, }; VkDescriptorSetAllocateInfo set_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .pSetLayouts = layouts, .descriptorSetCount = 1, .descriptorPool = out->pool, .pNext = NULL, }; result = vkAllocateDescriptorSets(device, &set_info, &out->descriptor); if(result != VK_SUCCESS) { free(out->textures); vkDestroyDescriptorPool(device, out->pool, 0); return result; } return VK_SUCCESS; } VkResult create_graphics_pipeline( VkDevice device, VkExtent2D extent, VkRenderPass draw_render_pass, VkRenderPass offscreen_render_pass, GraphicsPipelineInfo pipeline_info, uint32_t max_frames_in_flight, GraphicsPipeline* out ) { if(out == NULL) { return VK_ERROR_VALIDATION_FAILED_EXT; } out->max_frames_in_flight = max_frames_in_flight; if(pipeline_info.set_layout != VK_NULL_HANDLE) { out->descriptors = malloc(sizeof(VkDescriptorSet)*max_frames_in_flight); if(out->descriptors == 0) { return VK_ERROR_OUT_OF_HOST_MEMORY; } for(uint32_t i = 0; i < max_frames_in_flight; i++) { out->descriptors[i] = VK_NULL_HANDLE; } } VkDescriptorSetLayout all_layouts[2] = {pipeline_info.scene_layout, pipeline_info.set_layout}; uint32_t num_sets = 1; if(pipeline_info.set_layout != VK_NULL_HANDLE) { num_sets += 1; } VkPushConstantRange pcr = { .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .offset = 0, .size = sizeof(struct ScenePC) + sizeof(VkDeviceAddress), }; VkPipelineLayoutCreateInfo layout_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = num_sets, .pSetLayouts = all_layouts, .pushConstantRangeCount = 1, .pPushConstantRanges = &pcr, }; VkResult result = vkCreatePipelineLayout(device, &layout_info, 0, &out->layout); if(result != VK_SUCCESS) { return result; } VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, }; uint32_t dynamic_state_count = sizeof(dynamic_states)/sizeof(VkDynamicState); VkPipelineDynamicStateCreateInfo dynamic_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = dynamic_state_count, .pDynamicStates = dynamic_states, }; VkPipelineInputAssemblyStateCreateInfo input_assembly_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .primitiveRestartEnable = VK_FALSE, }; VkViewport viewport = { .x = 0.0f, .y = 0.0f, .width = (float)(extent.width), .height = (float)(extent.height), .minDepth = 0.0f, .maxDepth = 1.0f, }; VkRect2D scissor = { .offset = { .x = 0, .y = 0, }, .extent = extent, }; VkPipelineViewportStateCreateInfo viewport_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .pViewports = &viewport, .scissorCount = 1, .pScissors = &scissor, }; VkPipelineRasterizationStateCreateInfo raster_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .depthClampEnable = VK_FALSE, .rasterizerDiscardEnable = VK_FALSE, .polygonMode = VK_POLYGON_MODE_FILL, .lineWidth = 1.0f, .cullMode = VK_CULL_MODE_BACK_BIT, .frontFace = VK_FRONT_FACE_CLOCKWISE, .depthBiasEnable = VK_FALSE, .depthBiasConstantFactor = 0.0f, .depthBiasClamp = 0.0f, .depthBiasSlopeFactor = 0.0f, }; VkPipelineMultisampleStateCreateInfo multisample_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .sampleShadingEnable = VK_FALSE, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, .minSampleShading = 1.0f, .pSampleMask = 0, .alphaToCoverageEnable = VK_FALSE, .alphaToOneEnable = VK_FALSE, }; VkPipelineDepthStencilStateCreateInfo depth_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .depthTestEnable = VK_TRUE, .depthWriteEnable = VK_TRUE, .depthCompareOp = VK_COMPARE_OP_LESS, .depthBoundsTestEnable = VK_FALSE, .maxDepthBounds = 1.0f, .minDepthBounds = 0.0f, .stencilTestEnable = VK_FALSE, .front = {}, .back = {}, }; VkPipelineColorBlendAttachmentState color_blend_attachment = { .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, .blendEnable = VK_TRUE, .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, .colorBlendOp = VK_BLEND_OP_ADD, .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .alphaBlendOp = VK_BLEND_OP_ADD, }; VkPipelineColorBlendStateCreateInfo color_blend_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_COPY, .attachmentCount = 1, .pAttachments = &color_blend_attachment, .blendConstants[0] = 0.0f, .blendConstants[1] = 0.0f, .blendConstants[2] = 0.0f, .blendConstants[3] = 0.0f, }; VkGraphicsPipelineCreateInfo draw_pipeline_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .stageCount = pipeline_info.shader_stages_count, .pStages = pipeline_info.shader_stages, .pVertexInputState = &pipeline_info.input_info, .pInputAssemblyState = &input_assembly_info, .pViewportState = &viewport_state, .pRasterizationState = &raster_info, .pColorBlendState = &color_blend_info, .pDynamicState = &dynamic_info, .pDepthStencilState = &depth_info, .pMultisampleState = &multisample_info, .layout = out->layout, .renderPass = draw_render_pass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; VkPipelineColorBlendAttachmentState offscreen_attachment_states[] = { { .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, .blendEnable = VK_FALSE, }, }; VkPipelineColorBlendStateCreateInfo offscreen_blend_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_COPY, .attachmentCount = sizeof(offscreen_attachment_states)/sizeof(VkPipelineColorBlendAttachmentState), .pAttachments = offscreen_attachment_states, .blendConstants[0] = 0.0f, .blendConstants[1] = 0.0f, .blendConstants[2] = 0.0f, .blendConstants[3] = 0.0f, }; VkGraphicsPipelineCreateInfo offscreen_pipeline_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .stageCount = pipeline_info.shader_stages_count, .pStages = pipeline_info.shader_stages, // TODO: offscreen stages .pVertexInputState = &pipeline_info.input_info, .pInputAssemblyState = &input_assembly_info, .pViewportState = &viewport_state, .pRasterizationState = &raster_info, .pColorBlendState = &offscreen_blend_info, .pDynamicState = &dynamic_info, .pDepthStencilState = &depth_info, .pMultisampleState = &multisample_info, .layout = out->layout, .renderPass = offscreen_render_pass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &draw_pipeline_info, 0, &out->pipeline); if(result != VK_SUCCESS) { return result; } result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &offscreen_pipeline_info, 0, &out->offscreen_pipeline); if(result != VK_SUCCESS) { return result; } return VK_SUCCESS; } VkResult create_simple_mesh_pipeline(VkDevice device, VkExtent2D extent, VkRenderPass render_pass, VkRenderPass offscreen_render_pass, VkDescriptorSetLayout scene_layout, uint32_t max_frames_in_flight, GraphicsPipeline* out) { if(out == NULL) { return VK_ERROR_VALIDATION_FAILED_EXT; } VkShaderModule vert_shader = load_shader_file("shader_src/basic.vert.spv", device); VkShaderModule frag_shader = load_shader_file("shader_src/basic.frag.spv", device); VkPipelineShaderStageCreateInfo shader_stages[2] = { { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vert_shader, .pName = "main", }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = frag_shader, .pName = "main", }, }; VkVertexInputBindingDescription bindings[1] = { { .binding = 0, // Which buffer 'binding' to use .stride = sizeof(struct Vertex), // How many bytes to increase the index between instance .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, // Whether an instance is a vertex or an index }, }; VkVertexInputAttributeDescription attributes[2] = { { .binding = 0, // Which buffer 'binding' to use .location = 0, // Which 'location' to export as to shader .format = VK_FORMAT_R32G32B32_SFLOAT, // What format to interpret as for shader .offset = offsetof(struct Vertex, pos), // What offset from instance start }, { .binding = 0, .location = 1, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(struct Vertex, color), }, }; VkPipelineVertexInputStateCreateInfo input_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexBindingDescriptions = bindings, .vertexBindingDescriptionCount = sizeof(bindings)/sizeof(VkVertexInputBindingDescription), .pVertexAttributeDescriptions = attributes, .vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription), }; GraphicsPipelineInfo pipeline_info = { .set_layout = VK_NULL_HANDLE, .shader_stages_count = sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), .shader_stages = shader_stages, .scene_layout = scene_layout, .input_info = input_info, }; return create_graphics_pipeline(device, extent, render_pass, offscreen_render_pass, pipeline_info, max_frames_in_flight, out); } VkResult create_texture_mesh_pipeline(VkDevice device, VkPhysicalDeviceMemoryProperties memories, VkExtent2D extent, VkRenderPass render_pass, VkRenderPass offscreen_render_pass, VkDescriptorSetLayout scene_layout, uint32_t max_frames_in_flight, VkCommandPool transfer_pool, Queue transfer_queue, Queue graphics_queue, VkCommandPool graphics_pool, GraphicsPipeline* out) { if(out == NULL) { return VK_ERROR_VALIDATION_FAILED_EXT; } VkShaderModule vert_shader = load_shader_file("shader_src/texture.vert.spv", device); VkShaderModule frag_shader = load_shader_file("shader_src/texture.frag.spv", device); VkPipelineShaderStageCreateInfo shader_stages[2] = { { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vert_shader, .pName = "main", }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = frag_shader, .pName = "main", }, }; VkVertexInputBindingDescription bindings[] = { { .binding = 0, .stride = sizeof(struct TextureVertex), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, }, }; VkVertexInputAttributeDescription attributes[] = { { .binding = 0, .location = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(struct TextureVertex, pos), }, { .binding = 0, .location = 1, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(struct TextureVertex, color), }, { .binding = 0, .location = 2, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(struct TextureVertex, tex), }, }; // TODO: use bindless descriptors for textures, so each draw command will bind a large buffer that is indexed by object ID to get the address of the texture in GPU memory VkDescriptorSetLayoutBinding set_bindings[] = { { .binding = 0, .descriptorCount = 1000, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .pImmutableSamplers = 0, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, }, }; VkDescriptorBindingFlags set_binding_flags[] = { VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT, }; VkDescriptorSetLayoutBindingFlagsCreateInfo set_flags_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, .bindingCount = sizeof(set_bindings)/sizeof(VkDescriptorSetLayoutBinding), .pBindingFlags = set_binding_flags, }; VkDescriptorSetLayoutCreateInfo set_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .pBindings = set_bindings, .bindingCount = sizeof(set_bindings)/sizeof(VkDescriptorSetLayoutBinding), .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, .pNext = &set_flags_info, }; VkDescriptorSetLayout set_layout = VK_NULL_HANDLE; VkResult result = vkCreateDescriptorSetLayout(device, &set_info, 0, &set_layout); if(result != VK_SUCCESS) { return result; } TextureSet texture_set = {0}; result = create_texture_set(device, set_layout, 1000, &texture_set); if(result != VK_SUCCESS) { return result; } VkPipelineVertexInputStateCreateInfo input_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexBindingDescriptions = bindings, .vertexBindingDescriptionCount = sizeof(bindings)/sizeof(VkVertexInputBindingDescription), .pVertexAttributeDescriptions = attributes, .vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription), }; GraphicsPipelineInfo pipeline_info = { .set_layout = set_layout, .shader_stages_count = sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), .shader_stages = shader_stages, .scene_layout = scene_layout, .input_info = input_info, }; GPUPage* memory = NULL; result = gpu_page_allocate(device, memories, 100000, 0xFFFFFFFF, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 0, &memory); if(result != VK_SUCCESS) { return result; } result = create_graphics_pipeline(device, extent, render_pass, offscreen_render_pass, pipeline_info, max_frames_in_flight, out); if(result != VK_SUCCESS) { return result; } VkExtent2D texture_size = { .width = 10, .height = 10, }; (void)texture_size; struct __attribute__((__packed__)) texel { uint8_t r; uint8_t g; uint8_t b; uint8_t a; }; struct texel WHT = {255, 255, 255, 255}; struct texel BLK = {0, 0, 0, 255}; struct texel RED = {255, 0, 0, 255}; struct texel GRN = {0, 255, 0, 255}; struct texel BLU = {0, 0, 255, 255}; struct texel texture_data_0[100] = { RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, BLK, RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, BLK, RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, WHT, RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, WHT, RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, BLK, RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, BLK, RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, WHT, RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, WHT, RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, BLK, RED, WHT, GRN, WHT, BLU, WHT, RED, WHT, GRN, BLK, }; struct texel texture_data_1[100] = { RED, RED, RED, RED, RED, RED, RED, RED, RED, RED, RED, WHT, WHT, WHT, WHT, WHT, WHT, WHT, WHT, RED, RED, WHT, GRN, GRN, GRN, GRN, GRN, GRN, WHT, RED, RED, WHT, GRN, BLU, BLU, BLU, BLU, GRN, WHT, RED, RED, WHT, GRN, BLU, BLK, BLK, BLU, GRN, WHT, RED, RED, WHT, GRN, BLU, BLK, BLK, BLU, GRN, WHT, RED, RED, WHT, GRN, BLU, BLU, BLU, BLU, GRN, WHT, RED, RED, WHT, GRN, GRN, GRN, GRN, GRN, GRN, WHT, RED, RED, WHT, WHT, WHT, WHT, WHT, WHT, WHT, WHT, RED, RED, RED, RED, RED, RED, RED, RED, RED, RED, RED, }; GPUPage* texture_memory = NULL; result = gpu_page_allocate(device, memories, 100000, 0xFFFFFFFF, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, 0, &texture_memory); if(result != VK_SUCCESS) { return result; } GPUPage* staging_memory = NULL; result = gpu_page_allocate(device, memories, 100000, 0xFFFFFFFF, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 0, &staging_memory); if(result != VK_SUCCESS) { return result; } GPUBuffer staging = {0}; result = gpu_buffer_malloc(device, staging_memory, 100000, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, &staging); if(result != VK_SUCCESS) { return result; } Texture test_texture_0 = load_texture(device, texture_memory, staging, transfer_pool, transfer_queue, graphics_pool, graphics_queue, texture_size, VK_FORMAT_R8G8B8A8_SRGB, texture_data_0); Texture test_texture_1 = load_texture(device, texture_memory, staging, transfer_pool, transfer_queue, graphics_pool, graphics_queue, texture_size, VK_FORMAT_R8G8B8A8_SRGB, texture_data_1); VkDescriptorImageInfo image_info_0 = { .sampler = test_texture_0.sampler, .imageView = test_texture_0.view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; VkDescriptorImageInfo image_info_1 = { .sampler = test_texture_1.sampler, .imageView = test_texture_1.view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; VkWriteDescriptorSet descriptor_write = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = texture_set.descriptor, .dstBinding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .pBufferInfo = 0, .pTexelBufferView = 0, }; descriptor_write.pImageInfo = &image_info_0; descriptor_write.dstArrayElement = 0; vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, 0); descriptor_write.pImageInfo = &image_info_1; descriptor_write.dstArrayElement = 1; vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, 0); for(uint32_t i = 0; i < out->max_frames_in_flight; i++) { out->descriptors[i] = texture_set.descriptor; fprintf(stderr, "descriptor[%d]: %p\n", i, out->descriptors[i]); } return VK_SUCCESS; } VkResult command_copy_to_buffer(VkDevice device, GPUBuffer staging, VkBuffer destination, void* data, VkDeviceSize size, VkDeviceSize offset, VkCommandPool pool, Queue queue) { memcpy(staging.page->ptr + staging.memory->offset, data, size); VkCommandBuffer command_buffer = command_begin_single(device, pool); if(command_buffer == VK_NULL_HANDLE) { return VK_ERROR_UNKNOWN; } VkBufferCopy region = { .srcOffset = 0, .dstOffset = offset, .size = size, }; vkCmdCopyBuffer(command_buffer, staging.handle, destination, 1, ®ion); return command_end_single(device, command_buffer, pool, queue); } Mesh* load_mesh_to_buffer(VkDevice device, GPUPage* page, GPUBuffer staging, uint32_t vertex_count, uint32_t vertex_stride, void* vertex_data, uint32_t index_count, uint32_t index_stride, void* index_data, VkCommandPool pool, Queue queue) { GPUBuffer vertex_buffer = {0}; GPUBuffer index_buffer = {0}; VkResult result = gpu_buffer_malloc(device, page, vertex_count*vertex_stride, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, &vertex_buffer); if(result != VK_SUCCESS) { fprintf(stderr, "Failed to create vertex buffer\n"); return NULL; } result = gpu_buffer_malloc(device, page, sizeof(uint16_t)*index_count, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, &index_buffer); if(result != VK_SUCCESS) { fprintf(stderr, "Failed to create index buffer\n"); return NULL; } result = command_copy_to_buffer(device, staging, vertex_buffer.handle, vertex_data, vertex_count*vertex_stride, 0, pool, queue); if(result != VK_SUCCESS) { fprintf(stderr, "Failed to copy to vertex buffer\n"); return NULL; } result = command_copy_to_buffer(device, staging, index_buffer.handle, index_data, index_stride*index_count, 0, pool, queue); if(result != VK_SUCCESS) { fprintf(stderr, "Failed to copy to index buffer\n"); return NULL; } Mesh* mesh = malloc(sizeof(Mesh)); if(mesh == NULL) { return NULL; } mesh->vertex_buffer = vertex_buffer; mesh->vertex_count = vertex_count; mesh->index_buffer = index_buffer; mesh->index_count = index_count; return mesh; } VulkanContext* init_vulkan(GLFWwindow* window, uint32_t max_frames_in_flight) { VulkanContext* context = (VulkanContext*)malloc(sizeof(VulkanContext)); VkResult result = create_instance(&context->instance); if(result != VK_SUCCESS) { fprintf(stderr, "failed to initialize vulkan instance\n"); return 0; } result = create_debug_messenger(context->instance, &context->debug_messenger); if(result != VK_SUCCESS) { fprintf(stderr, "failed to initialize vulkan debug messenger\n"); return 0; } result = get_best_physical_device(context->instance, &context->physical_device); if(result != VK_SUCCESS) { fprintf(stderr, "failed to pick vulkan physical device\n"); return 0; } vkGetPhysicalDeviceMemoryProperties(context->physical_device, &context->memories); result = glfwCreateWindowSurface(context->instance, window, 0, &context->surface); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create vulkan surface\n"); return 0; } result = create_logical_device(context->physical_device, context->surface, &context->graphics_queue, &context->present_queue, &context->transfer_queue, &context->device); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create vulkan logical device\n"); return 0; } result = get_swapchain_details(context->physical_device, context->surface, &context->swapchain_details); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create vulkan logical device\n"); return 0; } 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); VkSwapchainKHR swapchain = 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, VK_NULL_HANDLE); if(swapchain == VK_NULL_HANDLE) { fprintf(stderr, "failed to create vulkan swapchain\n"); return 0; } else { context->swapchain = swapchain; } SwapchainImages swapchain_images = get_swapchain_images(context->device, context->swapchain); if(swapchain_images.count == 0) { fprintf(stderr, "failed to get vulkan swapchain images\n"); return 0; } else { context->swapchain_image_count = swapchain_images.count; context->swapchain_images = swapchain_images.images; } VkImageView* image_views = create_image_views(context->device, context->swapchain_image_count, context->swapchain_images, context->swapchain_format); if(image_views == 0) { fprintf(stderr, "failed to create vulkan image views\n"); return 0; } else { context->swapchain_image_views = image_views; } VkFormat requested[] = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }; VkFormat depth_format = find_depth_format(context->physical_device, 3, requested, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); if(depth_format == VK_FORMAT_MAX_ENUM) { fprintf(stderr, "failed to find a suitable depth image format\n"); return 0; } else { context->depth_format = depth_format; } 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) { fprintf(stderr, "failed to create extra graphics command pool\n"); return 0; } if(create_depth_image(context) != 0) { fprintf(stderr, "failed to create depth image\n"); return 0; } VkRenderPass render_pass = create_render_pass(context->device, context->swapchain_format, context->depth_format); if(render_pass == VK_NULL_HANDLE) { fprintf(stderr, "failed to create vulkan render pass\n"); return 0; } else { context->render_pass = render_pass; } VkFramebuffer* framebuffers = create_swapchain_framebuffers(context->device, context->swapchain_image_count, context->swapchain_image_views, context->depth_image_view, context->render_pass, context->swapchain_extent); if(framebuffers == 0) { fprintf(stderr, "failed to create vulkan framebuffers\n"); return 0; } else { context->swapchain_framebuffers = framebuffers; } 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, }; VkCommandPool graphics_command_pool; result = vkCreateCommandPool(context->device, &graphics_pool_info, 0, &graphics_command_pool); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create vulkan graphics command pool"); return 0; } else { context->graphics_command_pool = graphics_command_pool; } VkCommandPoolCreateInfo transfer_pool_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, .queueFamilyIndex = context->transfer_queue.family, }; VkCommandPool transfer_command_pool; result = vkCreateCommandPool(context->device, &transfer_pool_info, 0, &transfer_command_pool); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create vulkan transfer command pool"); return 0; } else { context->transfer_command_pool = transfer_command_pool; } context->max_frames_in_flight = max_frames_in_flight; VkCommandBuffer* swapchain_command_buffers = create_command_buffers(context->device, context->graphics_command_pool, max_frames_in_flight); if(swapchain_command_buffers == VK_NULL_HANDLE) { fprintf(stderr, "failed to create vulkan swapchain command buffer\n"); return 0; } else { context->swapchain_command_buffers = swapchain_command_buffers; } context->offscreen_command_buffers = create_command_buffers(context->device, context->graphics_command_pool, context->max_frames_in_flight); if(swapchain_command_buffers == VK_NULL_HANDLE) { fprintf(stderr, "failed to create vulkan swapchain command buffer\n"); return 0; } VkSemaphore* ia_semaphores = create_semaphores(context->device, 0, max_frames_in_flight); if(ia_semaphores == 0) { fprintf(stderr, "failed to create vulkan image available semaphores\n"); return 0; } else { context->image_available_semaphores = ia_semaphores; } VkSemaphore* rf_semaphores = create_semaphores(context->device, 0, max_frames_in_flight); if(rf_semaphores == 0) { fprintf(stderr, "failed to create vulkan render finished semaphores\n"); return 0; } else { context->render_finished_semaphores = rf_semaphores; } context->offscreen_complete_semaphores = create_semaphores(context->device, 0, 1); if(context->offscreen_complete_semaphores == NULL) { return 0; } VkFence* if_fences = create_fences(context->device, VK_FENCE_CREATE_SIGNALED_BIT, max_frames_in_flight); if(if_fences == 0) { fprintf(stderr, "failed to create vulkan in flight fence\n"); return 0; } else { context->in_flight_fences = if_fences; } result = gpu_page_allocate(context->device, context->memories, 15360000*3, 0xFFFFFFFF, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, 0, &context->g_buffer_page); if(result != VK_SUCCESS) { return 0; } VkImageCreateInfo g_pos_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, .imageType = VK_IMAGE_TYPE_2D, .extent = { .width = context->swapchain_extent.width, .height = context->swapchain_extent.height, .depth = 1, }, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, .format = VK_FORMAT_R16G16B16A16_SFLOAT, }; result = gpu_image_malloc(context->device, context->g_buffer_page, &g_pos_info, &context->g_image_position); if(result != VK_SUCCESS) { fprintf(stderr, "failed to allocate image\n"); return 0; } VkImageViewCreateInfo g_pos_view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = context->g_image_position.handle, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = VK_FORMAT_R16G16B16A16_SFLOAT, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; result = vkCreateImageView(context->device, &g_pos_view_info, 0, &context->g_image_view_position); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create image view\n"); return 0; } VkExtent3D offscreen_extent = { .width = context->swapchain_extent.width, .height = context->swapchain_extent.height, .depth = 1, }; VkImageCreateInfo offscreen_depth_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .pNext = NULL, .extent = offscreen_extent, .mipLevels = 1, .arrayLayers = 1, .format = context->depth_format, .tiling = VK_IMAGE_TILING_OPTIMAL, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .samples = VK_SAMPLE_COUNT_1_BIT, .flags = 0, }; result = gpu_image_malloc(context->device, context->g_buffer_page, &offscreen_depth_info, &context->g_image_depth); if(result != VK_SUCCESS) { fprintf(stderr, "failed to allocate g_image_depth\n"); return 0; } VkImageViewCreateInfo g_depth_view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = context->g_image_depth.handle, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = context->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(context->device, &g_depth_view_info, 0, &context->g_image_view_depth); if(result != VK_SUCCESS) { fprintf(stderr, "Failed to allocate g_image_view_depth\n"); return 0; } result = command_transition_image_layout(context->device, context->extra_graphics_pool, context->graphics_queue, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, context->g_image_depth.handle, 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) { fprintf(stderr, "Failed to transition g_image_depth to VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL\n"); return 0; } VkAttachmentDescription offscreen_attachments[] = { { .format = VK_FORMAT_R16G16B16A16_SFLOAT, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }, { .format = context->depth_format, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, }, }; VkAttachmentReference offscreen_attachment_refs[] = { { .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, } }; VkAttachmentReference offscreen_depth_reference = { .attachment = 1, .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, }; VkSubpassDescription offscreen_pass_descs[] = { { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .colorAttachmentCount = sizeof(offscreen_attachment_refs)/sizeof(VkAttachmentReference), .pColorAttachments = offscreen_attachment_refs, .pDepthStencilAttachment = &offscreen_depth_reference, }, }; VkSubpassDependency offscreen_pass_dependencies[] = { { .srcSubpass = VK_SUBPASS_EXTERNAL, .dstSubpass = 0, .srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT, .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, }, { .srcSubpass = 0, .dstSubpass = VK_SUBPASS_EXTERNAL, .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, .dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, }, }; VkRenderPassCreateInfo offscreen_pass_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = sizeof(offscreen_attachments)/sizeof(VkAttachmentDescription), .pAttachments = offscreen_attachments, .subpassCount = sizeof(offscreen_pass_descs)/sizeof(VkSubpassDescription), .pSubpasses = offscreen_pass_descs, .dependencyCount = sizeof(offscreen_pass_dependencies)/sizeof(VkSubpassDependency), .pDependencies = offscreen_pass_dependencies, }; result = vkCreateRenderPass(context->device, &offscreen_pass_info, 0, &context->g_renderpass); if(result != VK_SUCCESS) { fprintf(stderr, "Failed to create offscreen renderpass\n"); return 0; } VkImageView offscreen_fb_attachments[2] = { context->g_image_view_position, context->g_image_view_depth, }; VkFramebufferCreateInfo offscreen_framebuffer_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .renderPass = context->g_renderpass, .attachmentCount = sizeof(offscreen_fb_attachments)/sizeof(VkImageView), .pAttachments = offscreen_fb_attachments, .width = context->swapchain_extent.width, .height = context->swapchain_extent.height, .layers = 1, }; result = vkCreateFramebuffer(context->device, &offscreen_framebuffer_info, 0, &context->g_framebuffer); if(result != VK_SUCCESS) { fprintf(stderr, "failed to allocate offscreen framebuffer\n"); return 0; } return context; } SceneContext create_scene_context(VkDevice device, VkPhysicalDeviceMemoryProperties memories, uint32_t max_frames_in_flight) { SceneContext ret = { .pool = VK_NULL_HANDLE, .descriptor_layout = VK_NULL_HANDLE, .descriptors = 0, .ubos = 0, .ubo_ptrs = 0, }; VkDescriptorPoolSize pool_sizes[] = { { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = max_frames_in_flight, } }; VkDescriptorPoolCreateInfo pool_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .poolSizeCount = 1, .pPoolSizes = pool_sizes, .maxSets = max_frames_in_flight, }; VkDescriptorPool pool; VkResult result = vkCreateDescriptorPool(device, &pool_info, 0, &pool); if(result != VK_SUCCESS) { return ret; } VkDescriptorSetLayoutBinding layout_bindings[] = { { .binding = 0, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, .pImmutableSamplers = 0, } }; VkDescriptorSetLayoutCreateInfo layout_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = 1, .pBindings = layout_bindings, }; VkDescriptorSetLayout layout; result = vkCreateDescriptorSetLayout(device, &layout_info, 0, &layout); if(result != VK_SUCCESS) { vkDestroyDescriptorPool(device, pool, 0); return ret; } VkDescriptorSetLayout* layouts = malloc(sizeof(VkDescriptorSetLayout)*max_frames_in_flight); if(layouts == 0) { vkDestroyDescriptorPool(device, pool, 0); return ret; } for(uint32_t i = 0; i < max_frames_in_flight; i++) { layouts[i] = layout; } VkDescriptorSet* sets = malloc(sizeof(VkDescriptorSet)*max_frames_in_flight); if(sets == 0) { free(layouts); vkDestroyDescriptorPool(device, pool, 0); return ret; } VkDescriptorSetAllocateInfo set_alloc_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorPool = pool, .descriptorSetCount = max_frames_in_flight, .pSetLayouts = layouts, }; result = vkAllocateDescriptorSets(device, &set_alloc_info, sets); free(layouts); if(result != VK_SUCCESS) { free(sets); vkDestroyDescriptorPool(device, pool, 0); return ret; } GPUPage* scene_ubo_memory = NULL; result = gpu_page_allocate(device, memories, 1000, 0xFFFFFFFF, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 0, &scene_ubo_memory); if(result != VK_SUCCESS) { return ret; } GPUBuffer* ubos = malloc(sizeof(GPUBuffer)*max_frames_in_flight); if(ubos == NULL) { return ret; } for(uint32_t i = 0; i < max_frames_in_flight; i++) { result = gpu_buffer_malloc(device, scene_ubo_memory, sizeof(struct SceneUBO), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, &ubos[i]); if(result != VK_SUCCESS) { free(sets); free(ubos); vkFreeDescriptorSets(device, pool, max_frames_in_flight, sets); vkDestroyDescriptorPool(device, pool, 0); return ret; } } void** ubo_ptrs = malloc(sizeof(void*)*max_frames_in_flight); if(ubo_ptrs == 0) { free(sets); vkFreeDescriptorSets(device, pool, max_frames_in_flight, sets); vkDestroyDescriptorPool(device, pool, 0); free(ubos); return ret; } for(uint32_t i = 0; i < max_frames_in_flight; i++) { ubo_ptrs[i] = ubos[i].page->ptr + ubos[i].memory->offset; VkDescriptorBufferInfo buffer_info = { .buffer = ubos[i].handle, .offset = 0, .range = sizeof(struct SceneUBO), }; VkWriteDescriptorSet write = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = sets[i], .dstBinding = 0, .dstArrayElement = 0, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, .pBufferInfo = &buffer_info, }; vkUpdateDescriptorSets(device, 1, &write, 0, 0); } SceneContext scene = { .pool = pool, .descriptor_layout = layout, .descriptors = sets, .ubos = ubos, .ubo_ptrs = ubo_ptrs, .pcr_size = sizeof(struct ScenePC), }; return scene; } struct { bool forward; bool backward; bool left; bool right; bool up; bool down; bool turn_left; bool turn_right; bool turn_up; bool turn_down; bool roll_left; bool roll_right; } key_flags = { .forward = false, .backward = false, .left = false, .right = false, .turn_left = false, .turn_right = false, .turn_up = false, .turn_down = false, .roll_left = false, .roll_right = false, }; void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { (void)scancode; (void)window; (void)mods; switch(key) { case GLFW_KEY_W: if(action == GLFW_PRESS) { key_flags.forward = true; } else if(action == GLFW_RELEASE) { key_flags.forward = false; } break; case GLFW_KEY_A: if(action == GLFW_PRESS) { key_flags.left = true; } else if(action == GLFW_RELEASE) { key_flags.left = false; } break; case GLFW_KEY_S: if(action == GLFW_PRESS) { key_flags.backward = true; } else if(action == GLFW_RELEASE) { key_flags.backward = false; } break; case GLFW_KEY_D: if(action == GLFW_PRESS) { key_flags.right = true; } else if(action == GLFW_RELEASE) { key_flags.right = false; } break; case GLFW_KEY_SPACE: if(action == GLFW_PRESS) { key_flags.up = true; } else if(action == GLFW_RELEASE) { key_flags.up = false; } break; case GLFW_KEY_LEFT_SHIFT: if(action == GLFW_PRESS) { key_flags.down = true; } else if(action == GLFW_RELEASE) { key_flags.down = false; } break; case GLFW_KEY_RIGHT: if(action == GLFW_PRESS) { key_flags.turn_right = true; } else if(action == GLFW_RELEASE) { key_flags.turn_right = false; } break; case GLFW_KEY_LEFT: if(action == GLFW_PRESS) { key_flags.turn_left = true; } else if(action == GLFW_RELEASE) { key_flags.turn_left = false; } break; case GLFW_KEY_UP: if(action == GLFW_PRESS) { key_flags.turn_up = true; } else if(action == GLFW_RELEASE) { key_flags.turn_up = false; } break; case GLFW_KEY_DOWN: if(action == GLFW_PRESS) { key_flags.turn_down = true; } else if(action == GLFW_RELEASE) { key_flags.turn_down = false; } break; case GLFW_KEY_Q: if(action == GLFW_PRESS) { key_flags.roll_left = true; } else if(action == GLFW_RELEASE) { key_flags.roll_left = false; } break; case GLFW_KEY_E: if(action == GLFW_PRESS) { key_flags.roll_right = true; } else if(action == GLFW_RELEASE) { key_flags.roll_right = false; } break; } } vec3 world_position = {0.0f, 0.0f, 0.0f}; versor world_rotation = {-1.0f, 0.0f, 0.0f, 0.0f}; struct ScenePC get_scene_constants(vec3 world_position, versor world_rotation, float aspect_ratio, float time_delta) { vec3 movement_sum = {0.0f, 0.0f, 0.0f}; if(key_flags.forward) { movement_sum[2] -= 1 * time_delta; } if(key_flags.backward) { movement_sum[2] += 1 * time_delta; } if(key_flags.right) { movement_sum[0] += 1 * time_delta; } if(key_flags.left) { movement_sum[0] -= 1 * time_delta; } if(key_flags.up) { movement_sum[1] -= 1 * time_delta; } if(key_flags.down) { movement_sum[1] += 1 * time_delta; } vec3 eular_rotation = {0.0f, 0.0f, 0.0f}; if(key_flags.turn_right) { eular_rotation[0] -= 1 * time_delta; } if(key_flags.turn_left) { eular_rotation[0] += 1 * time_delta; } if(key_flags.turn_up) { eular_rotation[1] -= 1 * time_delta; } if(key_flags.turn_down) { eular_rotation[1] += 1 * time_delta; } if(key_flags.roll_right) { eular_rotation[2] += 1 * time_delta; } if(key_flags.roll_left) { eular_rotation[2] -= 1 * time_delta; } vec3 right = {1.0f, 0.0f, 0.0f}; vec3 up = {0.0f, 1.0f, 0.0f}; vec3 forward = {0.0f, 0.0f, 1.0f}; glm_quat_rotatev(world_rotation, right, right); glm_quat_rotatev(world_rotation, up, up); glm_quat_rotatev(world_rotation, forward, forward); versor relative_rotation_y; glm_quatv(relative_rotation_y, eular_rotation[1], right); versor relative_rotation_x; glm_quatv(relative_rotation_x, eular_rotation[0], up); versor relative_rotation_z; glm_quatv(relative_rotation_z, eular_rotation[2], forward); glm_quat_mul(relative_rotation_x, world_rotation, world_rotation); glm_quat_mul(relative_rotation_y, world_rotation, world_rotation); glm_quat_mul(relative_rotation_z, world_rotation, world_rotation); vec3 movement_rot; glm_quat_rotatev(world_rotation, movement_sum, movement_rot); glm_vec3_add(movement_rot, world_position, world_position); struct ScenePC constants = {}; glm_perspective(1.5708f, aspect_ratio, 0.01, 1000, constants.proj); glm_quat_look(world_position, world_rotation, constants.view); return constants; } VkResult draw_frame(VulkanContext* context, SceneContext* scene, uint32_t pipelines_count, GraphicsPipeline* pipelines, uint32_t* objects_counts, Object** objects, VkDeviceAddress object_buffer_addr) { struct ScenePC scene_constants = get_scene_constants(world_position, world_rotation, (float)context->swapchain_extent.width/(float)context->swapchain_extent.height, 0.01); VkResult result; result = vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX); if(result != VK_SUCCESS) { return result; } uint32_t image_index; 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 = vkResetFences(context->device, 1, &context->in_flight_fences[context->current_frame]); if(result != VK_SUCCESS) { return result; } result = vkResetCommandBuffer(context->swapchain_command_buffers[context->current_frame], 0); if(result != VK_SUCCESS) { return result; } result = command_draw_scene(pipelines_count, pipelines, objects_counts, objects, context->current_frame, scene->descriptors, &scene_constants, context->swapchain_command_buffers[context->current_frame], context->render_pass, context->swapchain_framebuffers[image_index], context->swapchain_extent, object_buffer_addr, 0); if(result != VK_SUCCESS) { return result; } result = vkResetCommandBuffer(context->offscreen_command_buffers[context->current_frame], 0); if(result != VK_SUCCESS) { return result; } result = command_draw_scene(pipelines_count, pipelines, objects_counts, objects, context->current_frame, scene->descriptors, &scene_constants, context->offscreen_command_buffers[context->current_frame], context->g_renderpass, context->g_framebuffer, context->swapchain_extent, object_buffer_addr, 1); if(result != VK_SUCCESS) { return result; } VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkSubmitInfo offscreen_submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, .pWaitSemaphores = &context->image_available_semaphores[context->current_frame], .pWaitDstStageMask = wait_stages, .commandBufferCount = 1, .pCommandBuffers = &context->offscreen_command_buffers[context->current_frame], .signalSemaphoreCount = 1, .pSignalSemaphores = &context->offscreen_complete_semaphores[0], }; VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, .pWaitSemaphores = &context->offscreen_complete_semaphores[0], .pWaitDstStageMask = wait_stages, .commandBufferCount = 1, .pCommandBuffers = &context->swapchain_command_buffers[context->current_frame], .signalSemaphoreCount = 1, .pSignalSemaphores = &context->render_finished_semaphores[context->current_frame], }; result = vkQueueSubmit(context->graphics_queue.handle, 1, &offscreen_submit_info, 0); if(result != VK_SUCCESS) { return result; } 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, }; return vkQueuePresentKHR(context->present_queue.handle, &present_info); } Object create_simple_mesh_object(PlyMesh ply_mesh, GraphicsPipeline* simple_mesh_pipeline, VkPhysicalDeviceMemoryProperties memories, VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue) { Object zero = {}; GPUPage* mesh_memory = NULL; VkResult result = gpu_page_allocate(device, memories, 100000, 0xFFFFFFFF, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, 0, &mesh_memory); if(result != VK_SUCCESS) { return zero; } GPUPage* transfer_memory = NULL; result = gpu_page_allocate(device, memories, 200000, 0xFFFFFFFF, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 0, &transfer_memory); if(result != VK_SUCCESS) { return zero; } GPUBuffer transfer_buffer = {0}; result = gpu_buffer_malloc(device, transfer_memory, 100000, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, &transfer_buffer); if(result != VK_SUCCESS) { return zero; } struct Vertex* tmp = malloc(sizeof(struct Vertex)*ply_mesh.vertex_count); for(uint32_t i = 0; i < ply_mesh.vertex_count; i++) { tmp[i].pos[0] = ply_mesh.position[i][0]; tmp[i].pos[1] = ply_mesh.position[i][1]; tmp[i].pos[2] = ply_mesh.position[i][2]; tmp[i].color[0] = ply_mesh.colour[i][0]; tmp[i].color[1] = ply_mesh.colour[i][1]; tmp[i].color[2] = ply_mesh.colour[i][2]; } Mesh* mesh = load_mesh_to_buffer(device, mesh_memory, transfer_buffer, ply_mesh.vertex_count, sizeof(struct Vertex), (void*)tmp, ply_mesh.index_count, sizeof(uint16_t), (void*)ply_mesh.index, transfer_pool, transfer_queue); free(tmp); if(mesh == 0) { return zero; } Object object = create_renderable(mesh, simple_mesh_pipeline); if(object.attributes.buckets == 0) { return zero; } Position* position = malloc(sizeof(Position)); if(position == 0) { return zero; } glm_quat_identity(position->rotation); position->scale[0] = 1.f; position->scale[1] = 1.f; position->scale[2] = 1.f; position->position[0] = 0.0f; position->position[1] = 0.0f; position->position[2] = 1.1f; bool map_result = map_add(&object.attributes, ATTRIBUTE_ID_POSITION, position); if(map_result == 0) { return zero; } return object; } Object create_texture_mesh_object(GraphicsPipeline* texture_mesh_pipeline, VkPhysicalDeviceMemoryProperties memories, VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue) { Object zero = {}; GPUPage* mesh_memory = NULL; VkResult result = gpu_page_allocate(device, memories, 100000, 0xFFFFFFFF, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, 0, &mesh_memory); if(result != VK_SUCCESS) { return zero; } GPUPage* transfer_memory = NULL; result = gpu_page_allocate(device, memories, 100000, 0xFFFFFFFF, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 0, &transfer_memory); if(result != VK_SUCCESS) { return zero; } GPUBuffer transfer_buffer = {0}; result = gpu_buffer_malloc(device, transfer_memory, 10000, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, &transfer_buffer); if(result != VK_SUCCESS) { return zero; } Mesh* mesh = load_mesh_to_buffer(device, mesh_memory, transfer_buffer, 4, sizeof(struct TextureVertex), (void*)texture_vertices, 6, sizeof(uint16_t), (void*)indices, transfer_pool, transfer_queue); if(mesh == 0) { return zero; } Object object = create_renderable(mesh, texture_mesh_pipeline); if(object.attributes.buckets == 0) { return zero; } Position* position = malloc(sizeof(Position)); if(position == 0) { return zero; } glm_quat_identity(position->rotation); position->scale[0] = 0.5f; position->scale[1] = 0.5f; position->scale[2] = 0.5f; position->position[0] = 0.0f; position->position[1] = 0.0f; position->position[2] = 1.0f; bool map_result = map_add(&object.attributes, ATTRIBUTE_ID_POSITION, position); if(map_result == 0) { return zero; } return object; } void main_loop(PlyMesh ply_mesh, GLFWwindow* window, VulkanContext* context) { SceneContext scene = create_scene_context(context->device, context->memories, context->max_frames_in_flight); if(scene.pool == VK_NULL_HANDLE) { return; } GraphicsPipeline simple_mesh_pipeline = {0}; GraphicsPipeline texture_mesh_pipeline = {0}; VkResult result = create_simple_mesh_pipeline(context->device, context->swapchain_extent, context->render_pass, context->g_renderpass, scene.descriptor_layout, context->max_frames_in_flight, &simple_mesh_pipeline); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create simple mesh material: %s\n", string_VkResult(result)); return; } Object triangle_object = create_simple_mesh_object(ply_mesh, &simple_mesh_pipeline, context->memories, context->device, context->transfer_command_pool, context->transfer_queue); if(triangle_object.attributes.buckets == 0) { fprintf(stderr, "failed to create simple mesh object\n"); return; } result = create_texture_mesh_pipeline(context->device, context->memories, context->swapchain_extent, context->render_pass, context->g_renderpass, scene.descriptor_layout, context->max_frames_in_flight, context->transfer_command_pool, context->transfer_queue, context->graphics_queue, context->extra_graphics_pool, &texture_mesh_pipeline); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create texture mesh material\n"); return; } Object triangle_object_textured = create_texture_mesh_object(&texture_mesh_pipeline, context->memories, context->device, context->transfer_command_pool, context->transfer_queue); if(triangle_object_textured.attributes.buckets == 0) { fprintf(stderr, "failed to create texture mesh object\n"); return; } Object* objects[] = {&triangle_object, &triangle_object_textured}; GraphicsPipeline pipelines[] = {simple_mesh_pipeline, texture_mesh_pipeline}; uint32_t objects_counts[] = {1, 1}; GPUPage* memory = NULL; result = gpu_page_allocate(context->device, context->memories, 100000, 0xFFFFFFFF, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, &memory); if(result != VK_SUCCESS) { return; } GPUBuffer buffer = {0}; result = gpu_buffer_malloc(context->device, memory, sizeof(mat4)*100, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, &buffer); if(result != VK_SUCCESS) { return; } mat4* model_1 = memory->ptr + buffer.memory->offset; mat4* model_2 = memory->ptr + buffer.memory->offset + sizeof(mat4); glm_mat4_identity(*model_1); glm_translate_x(*model_1, 1.0f); glm_mat4_identity(*model_2); glm_translate_x(*model_2, -1.0f); VkBufferDeviceAddressInfo addr_info = { .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = buffer.handle, }; VkDeviceAddress object_address = vkGetBufferDeviceAddress(context->device, &addr_info); context->current_frame = 0; while(!glfwWindowShouldClose(window)) { glfwPollEvents(); VkResult result = draw_frame(context, &scene, sizeof(pipelines)/sizeof(GraphicsPipeline), pipelines, objects_counts, objects, object_address); if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { vkDeviceWaitIdle(context->device); recreate_swapchain(context); } else if(result != VK_SUCCESS) { fprintf(stderr, "draw_frame error %d\n", result); return; } context->current_frame += 1; if(context->current_frame >= context->max_frames_in_flight) { context->current_frame = 0; } } vkDeviceWaitIdle(context->device); } void cleanup(GLFWwindow* window, VulkanContext* context) { if(context != 0) { if(context->instance != VK_NULL_HANDLE) { if(context->extra_graphics_pool != VK_NULL_HANDLE) { vkDestroyCommandPool(context->device, context->extra_graphics_pool, 0); } if(context->render_pass != VK_NULL_HANDLE) { vkDestroyRenderPass(context->device, context->render_pass, 0); } if(context->depth_image != VK_NULL_HANDLE) { vkDestroyImage(context->device, context->depth_image, 0); } for(uint32_t i = 0; i < context->swapchain_image_count; i++) { vkDestroyFramebuffer(context->device, context->swapchain_framebuffers[i], 0); vkDestroyImageView(context->device, context->swapchain_image_views[i], 0); } for(uint32_t i = 0; i < context->max_frames_in_flight; i++) { vkDestroySemaphore(context->device, context->image_available_semaphores[i], 0); vkDestroySemaphore(context->device, context->render_finished_semaphores[i], 0); vkDestroyFence(context->device, context->in_flight_fences[i], 0); } if(context->depth_image_view != VK_NULL_HANDLE) { vkDestroyImageView(context->device, context->depth_image_view, 0); } if(context->depth_image_memory != VK_NULL_HANDLE) { vkFreeMemory(context->device, context->depth_image_memory, 0); } if(context->graphics_command_pool != VK_NULL_HANDLE) { vkDestroyCommandPool(context->device, context->graphics_command_pool, 0); } if(context->transfer_command_pool != VK_NULL_HANDLE) { vkDestroyCommandPool(context->device, context->transfer_command_pool, 0); } if(context->swapchain_command_buffers != NULL) { free(context->swapchain_command_buffers); } 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(); } } int main() { PlyMesh monkey = ply_load_mesh("monkey.ply", default_ply_mappings); if(monkey.position == 0) { fprintf(stderr, "failed to load %s\n", "monkey.ply"); } GLFWwindow* window = init_window(800, 600); if(window == 0) { fprintf(stderr, "failed to initialize glfw window\n"); return 1; } VulkanContext* context = init_vulkan(window, 2); if (context == 0) { fprintf(stderr, "failed to initialize vulkan context\n"); return 2; } glfwSetKeyCallback(window, key_callback); main_loop(monkey, window, context); cleanup(window, context); return 0; }