// vim:fileencoding=utf-8:foldmethod=marker #include "wapp/wapp.h" #include "vulkan_profiles/vulkan_profiles.h" #include #include #include #include #include #include #include #include #include #include #define VMA_IMPLEMENTATION #include #include #include #include #include #include enum ExitCode { EXIT_CODE_SUCCESS, EXIT_CODE_SDL_INIT_FAILED, EXIT_CODE_VULKAN_LIB_LOAD_FAILED, EXIT_CODE_WINDOW_CREATION_FAILED, EXIT_CODE_SURFACE_CREATION_FAILED, EXIT_CODE_GET_WINDOW_SIZE_FAILED, EXIT_CODE_NO_INSTANCE_SUPPORT, EXIT_CODE_NO_PHYSICAL_DEVICES, EXIT_CODE_ALLOCATION_FAILURE, EXIT_CODE_NO_SUITABLE_PHYSICAL_DEVICE, EXIT_CODE_NO_PRESENTATION_SUPPORT, EXIT_CODE_NO_PHYSICAL_DEVICE_SUPPORT, EXIT_CODE_NO_QUEUE_FAMILIES, EXIT_CODE_NO_SUITABLE_QUEUE_FAMILY, EXIT_CODE_NO_SUITABLE_DEPTH_FORMAT, EXIT_CODE_MESH_LOAD_FAILED, }; struct Vertex { glm::vec3 pos; glm::vec3 normal; glm::vec2 uv; }; struct ShaderData { glm::mat4 projection; glm::mat4 view; glm::mat4 model[3]; glm::vec4 light_pos{ 0.0f, -10.0f, 10.0f, 0.0f }; u32 selected{ 1 }; }; struct ShaderDataBuffer { VmaAllocation allocation; VmaAllocationInfo allocation_info; VkBuffer buffer; VkDeviceAddress device_address; }; typedef VkPhysicalDevice *VkPhysicalDeviceArray; typedef VkQueueFamilyProperties2 *VkQueueFamilyProperties2Array; typedef VkImage *VkImageArray; typedef VkImageView *VkImageViewArray; typedef VkFormat *VkFormatArray; typedef Vertex *VertexArray; typedef ShaderDataBuffer *ShaderDataBufferArray; wapp_intern inline void check(VkResult result); wapp_intern inline void check_swapchain(VkResult result); wapp_intern inline void check(bool result, i32 code); wapp_intern constexpr u32 max_frames_in_flight = 2; wapp_intern bool running = true; wapp_intern bool update_swapchain = false; wapp_intern f32 display_scale = 1.0f; wapp_intern SDL_Window *window = nullptr; wapp_intern VkInstance instance = VK_NULL_HANDLE; wapp_intern VkPhysicalDevice physical_device = VK_NULL_HANDLE; wapp_intern VkSurfaceKHR surface = VK_NULL_HANDLE; wapp_intern u32 queue_family_index = 0; wapp_intern VkDevice device = VK_NULL_HANDLE; wapp_intern VkQueue queue = VK_NULL_HANDLE; wapp_intern VmaAllocator allocator = VK_NULL_HANDLE; wapp_intern glm::ivec2 window_size = {}; // NOTE (Abdelrahman): The following three variables are better queried from the physical device // to find the different supported values. For the purposes of this tutorial, we're using defaults // that are guaranteed by the spec to be available on any valid implementation. wapp_intern VkFormat image_format = VK_FORMAT_B8G8R8A8_SRGB; wapp_intern VkColorSpaceKHR colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; wapp_intern VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; wapp_intern VkSwapchainKHR swapchain = VK_NULL_HANDLE; wapp_intern u32 swapchain_image_count = 0; wapp_intern VkImageArray swapchain_images = nullptr; wapp_intern VkImageViewArray swapchain_views = nullptr; wapp_intern VkFormat depth_format = VK_FORMAT_UNDEFINED; wapp_intern VkImage depth_image = VK_NULL_HANDLE; wapp_intern VkImageView depth_view = VK_NULL_HANDLE; wapp_intern VmaAllocation depth_allocation = VK_NULL_HANDLE; wapp_intern VkBuffer vert_index_buf = VK_NULL_HANDLE; wapp_intern VmaAllocation vert_index_buf_alloc = VK_NULL_HANDLE; wapp_intern ShaderData shader_data = {}; wapp_intern ShaderDataBufferArray shader_data_bufs; int main() { // {{{ Initialisation Allocator arena = wapp_mem_arena_allocator_init(MiB(64)); check(SDL_Init(SDL_INIT_VIDEO), EXIT_CODE_SDL_INIT_FAILED); check(SDL_Vulkan_LoadLibrary(nullptr), EXIT_CODE_VULKAN_LIB_LOAD_FAILED); check(volkInitialize()); display_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); window = SDL_CreateWindow("How To Vulkan", (i32)(display_scale * 1920), (i32)(display_scale * 1080), SDL_WINDOW_VULKAN); check(window != nullptr, EXIT_CODE_WINDOW_CREATION_FAILED); // }}} // {{{ Get Vulkan Profile VkBool32 supported = VK_FALSE; VpProfileProperties profile = { .profileName = VP_LEARN_HOW_TO_VULKAN_2026_NAME, .specVersion = VP_LEARN_HOW_TO_VULKAN_2026_SPEC_VERSION, }; // }}} // {{{ Instance Creation // {{{ Get Platform Extensions u32 instance_extension_count = 0; const char *const *instance_extensions = SDL_Vulkan_GetInstanceExtensions(&instance_extension_count); // }}} // {{{ Check Instance Profile Support check(vpGetInstanceProfileSupport(NULL, &profile, &supported)); check(supported, EXIT_CODE_NO_INSTANCE_SUPPORT); // }}} VkApplicationInfo app_info = {}; app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app_info.apiVersion = VP_LEARN_HOW_TO_VULKAN_2026_MIN_API_VERSION; // {{{ Create Instance VkInstanceCreateInfo instance_info = {}; instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instance_info.pApplicationInfo = &app_info; instance_info.enabledExtensionCount = instance_extension_count; instance_info.ppEnabledExtensionNames = instance_extensions; VpInstanceCreateInfo vp_instance_info = {}; vp_instance_info.pCreateInfo = &instance_info; vp_instance_info.enabledFullProfileCount = 1; vp_instance_info.pEnabledFullProfiles = &profile; check(vpCreateInstance(&vp_instance_info, NULL, &instance)); // }}} volkLoadInstance(instance); // }}} wapp_mem_arena_allocator_temp_begin(&arena); // {{{ Physical Device Retrieval // {{{ Get Physical Device Count u32 physical_device_count = 0; check(vkEnumeratePhysicalDevices(instance, &physical_device_count, nullptr)); check(physical_device_count > 0, EXIT_CODE_NO_PHYSICAL_DEVICES); // }}} // {{{ Get All Physical Devices VkPhysicalDeviceArray physical_devices = wapp_array_alloc_capacity(VkPhysicalDevice, &arena, physical_device_count, ARRAY_INIT_FILLED); check(physical_devices != nullptr, EXIT_CODE_ALLOCATION_FAILURE); check(vkEnumeratePhysicalDevices(instance, &physical_device_count, physical_devices)); // Set the count in case it changes after the second call to vkEnumeratePhysicalDevices wapp_array_set_count(physical_devices, physical_device_count); // }}} // {{{ Choose Appropriate Physical Device i32 index = -1; for (u32 i = 0; i < wapp_array_count(physical_devices); ++i) { VkPhysicalDeviceProperties2 base_properties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 }; vkGetPhysicalDeviceProperties2(physical_devices[i], &base_properties); switch (base_properties.properties.deviceType) { case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { if (base_properties.properties.apiVersion >= VP_LEARN_HOW_TO_VULKAN_2026_MIN_API_VERSION) { index = i; } } break; case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { if (base_properties.properties.apiVersion >= VP_LEARN_HOW_TO_VULKAN_2026_MIN_API_VERSION) { index = i; } } break; default: continue; } } check(index != -1, EXIT_CODE_NO_SUITABLE_PHYSICAL_DEVICE); physical_device = physical_devices[index]; // {{{ Print Physical Device Information VkPhysicalDeviceDriverProperties p_device_driver_props = {}; p_device_driver_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; VkPhysicalDeviceProperties2 p_device_props = {}; p_device_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; p_device_props.pNext = &p_device_driver_props; vkGetPhysicalDeviceProperties2(physical_device, &p_device_props); u32 api_major = VK_API_VERSION_MAJOR(p_device_props.properties.apiVersion); u32 api_minor = VK_API_VERSION_MINOR(p_device_props.properties.apiVersion); u32 api_patch = VK_API_VERSION_PATCH(p_device_props.properties.apiVersion); u32 api_variant = VK_API_VERSION_VARIANT(p_device_props.properties.apiVersion); std::cout << "Selected GPU: " << p_device_props.properties.deviceName << '\n' << "Driver version: " << p_device_driver_props.driverInfo << '\n' << "API version: " << api_major << '.' << api_minor << '.' << api_patch << '.' << api_variant << '\n'; // }}} // }}} // {{{ Check Physical Device Profile Support check(vpGetPhysicalDeviceProfileSupport(instance, physical_device, &profile, &supported)); check(supported, EXIT_CODE_NO_PHYSICAL_DEVICE_SUPPORT); // }}} // }}} // {{{ Surface Creation check(SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface), EXIT_CODE_SURFACE_CREATION_FAILED); check(SDL_GetWindowSize(window, &window_size.x, &window_size.y), EXIT_CODE_GET_WINDOW_SIZE_FAILED); VkSurfaceCapabilitiesKHR surface_caps{}; check(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_caps)); // }}} // {{{ Queue Family Retrieval // {{{ Get Queue Family Count u32 queue_family_count = 0; vkGetPhysicalDeviceQueueFamilyProperties2(physical_device, &queue_family_count, nullptr); check(queue_family_count > 0, EXIT_CODE_NO_QUEUE_FAMILIES); // }}} // {{{ Get All Queue Families VkQueueFamilyProperties2Array queue_family_properties = wapp_array_alloc_capacity(VkQueueFamilyProperties2, &arena, queue_family_count, ARRAY_INIT_FILLED); check(queue_family_properties != nullptr, EXIT_CODE_ALLOCATION_FAILURE); for (u32 i = 0; i < wapp_array_count(queue_family_properties); ++i) { queue_family_properties[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; } vkGetPhysicalDeviceQueueFamilyProperties2(physical_device, &queue_family_count, queue_family_properties); // Set the count in case it changes after the second call to vkGetPhysicalDeviceQueueFamilyProperties2 wapp_array_set_count(queue_family_properties, queue_family_count); // }}} // {{{ Choose Appropriate Queue Family i32 family_index = -1; for (u32 i = 0; i < queue_family_count; ++i) { if ((queue_family_properties[i].queueFamilyProperties.queueFlags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT)) != 0) { family_index = i; break; } } check(family_index != -1, EXIT_CODE_NO_SUITABLE_QUEUE_FAMILY); queue_family_index = (u32)family_index; // }}} // {{{ Check Presentation Support check(SDL_Vulkan_GetPresentationSupport(instance, physical_device, queue_family_index), EXIT_CODE_NO_PRESENTATION_SUPPORT); // }}} // }}} wapp_mem_arena_allocator_temp_end(&arena); // {{{ Device And Queue Creation // {{{ Create Device f32 queue_priority = 1.0f; VkDeviceQueueCreateInfo device_queue_create_info = {}; device_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; device_queue_create_info.queueCount = 1; device_queue_create_info.queueFamilyIndex = queue_family_index; device_queue_create_info.pQueuePriorities = &queue_priority; VkDeviceCreateInfo device_create_info = {}; device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; device_create_info.queueCreateInfoCount = 1; device_create_info.pQueueCreateInfos = &device_queue_create_info; VpDeviceCreateInfo vp_device_create_info = {}; vp_device_create_info.pCreateInfo = &device_create_info; vp_device_create_info.enabledFullProfileCount = 1; vp_device_create_info.pEnabledFullProfiles = &profile; check(vpCreateDevice(physical_device, &vp_device_create_info, nullptr, &device)); // }}} // {{{ Get Queue vkGetDeviceQueue(device, queue_family_index, 0, &queue); // }}} volkLoadDevice(device); // }}} // {{{ Vulkan Memory Allocator (VMA) Setup VmaVulkanFunctions vk_functions = {}; vk_functions.vkGetInstanceProcAddr = vkGetInstanceProcAddr; vk_functions.vkGetDeviceProcAddr = vkGetDeviceProcAddr; vk_functions.vkCreateImage = vkCreateImage; VmaAllocatorCreateInfo allocator_create_info = {}; allocator_create_info.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT | VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT; allocator_create_info.instance = instance; allocator_create_info.physicalDevice = physical_device; allocator_create_info.device = device; allocator_create_info.pVulkanFunctions = &vk_functions; check(vmaCreateAllocator(&allocator_create_info, &allocator)); // }}} // {{{ Swapchain Creation // {{{ Calculate Extent VkExtent2D swapchain_extent = surface_caps.currentExtent; // Handle Wayland's special value (0xffffffff), which indicate that the surface size will // basically be determined by the size of the surface if (swapchain_extent.width == 0xffffffff) { swapchain_extent.width = (u32)window_size.x; swapchain_extent.height = (u32)window_size.y; } // }}} // {{{ Create Swapchain VkSwapchainCreateInfoKHR swapchain_create_info = {}; swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchain_create_info.surface = surface; swapchain_create_info.minImageCount = surface_caps.minImageCount; swapchain_create_info.imageFormat = image_format; swapchain_create_info.imageColorSpace = colorspace; swapchain_create_info.imageExtent = swapchain_extent; swapchain_create_info.imageArrayLayers = 1; swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchain_create_info.queueFamilyIndexCount = 1; swapchain_create_info.pQueueFamilyIndices = &queue_family_index; swapchain_create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swapchain_create_info.presentMode = present_mode; check(vkCreateSwapchainKHR(device, &swapchain_create_info, nullptr, &swapchain)); // }}} // {{{ Get Swapchain Images check(vkGetSwapchainImagesKHR(device, swapchain, &swapchain_image_count, nullptr)); swapchain_images = wapp_array_alloc_capacity(VkImage, &arena, swapchain_image_count, ARRAY_INIT_FILLED); check(swapchain_images != nullptr, EXIT_CODE_ALLOCATION_FAILURE); check(vkGetSwapchainImagesKHR(device, swapchain, &swapchain_image_count, swapchain_images)); // Set the count in case it changes after the second call to vkGetSwapchainImagesKHR wapp_array_set_count(swapchain_images, swapchain_image_count); // }}} // {{{ Create Swapchain Image Views swapchain_views = wapp_array_alloc_capacity(VkImageView, &arena, swapchain_image_count, ARRAY_INIT_FILLED); check(swapchain_views != nullptr, EXIT_CODE_ALLOCATION_FAILURE); for (u32 i = 0; i < swapchain_image_count; ++i) { VkImageViewCreateInfo view_create_info = {}; view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_create_info.image = swapchain_images[i]; view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; view_create_info.format = image_format; view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_create_info.subresourceRange.levelCount = 1; view_create_info.subresourceRange.layerCount = 1; check(vkCreateImageView(device, &view_create_info, nullptr, &swapchain_views[i])); } // }}} // }}} // {{{ Depth Attachment Setup // {{{ Check Supported Depth Formats // NOTE (Abdelrahman): Favour 24-bit format for performance VkFormatArray depth_formats = wapp_array(VkFormat, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT); for (u32 i = 0; i < wapp_array_count(depth_formats); ++i) { VkFormatProperties2 format_properties = { VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2 }; vkGetPhysicalDeviceFormatProperties2(physical_device, depth_formats[i], &format_properties); if (format_properties.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { depth_format = depth_formats[i]; break; } } check(depth_format != VK_FORMAT_UNDEFINED, EXIT_CODE_NO_SUITABLE_DEPTH_FORMAT); // }}} // {{{ Create Depth Image VkImageCreateInfo depth_image_create_info = {}; depth_image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; depth_image_create_info.imageType = VK_IMAGE_TYPE_2D; depth_image_create_info.format = depth_format; depth_image_create_info.extent = {swapchain_extent.width, swapchain_extent.height, 1}; depth_image_create_info.mipLevels = 1; depth_image_create_info.arrayLayers = 1; depth_image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; depth_image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; depth_image_create_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; depth_image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; depth_image_create_info.queueFamilyIndexCount = 1; depth_image_create_info.pQueueFamilyIndices = &queue_family_index; depth_image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VmaAllocationCreateInfo depth_alloc_create_info = {}; depth_alloc_create_info.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; depth_alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO; check(vmaCreateImage(allocator, &depth_image_create_info, &depth_alloc_create_info, &depth_image, &depth_allocation, nullptr)); // }}} // {{{ Create Depth Image View VkImageViewCreateInfo depth_view_create_info = {}; depth_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; depth_view_create_info.image = depth_image; depth_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; depth_view_create_info.format = depth_format; depth_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; depth_view_create_info.subresourceRange.levelCount = 1; depth_view_create_info.subresourceRange.layerCount = 1; check(vkCreateImageView(device, &depth_view_create_info, nullptr, &depth_view)); // }}} // }}} wapp_mem_arena_allocator_temp_begin(&arena); // {{{ Mesh Loading // {{{ Load Mesh Data tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; check(tinyobj::LoadObj(&attrib, &shapes, &materials, nullptr, nullptr, "assets/suzanne.obj"), EXIT_CODE_MESH_LOAD_FAILED); const VkDeviceSize index_count = shapes[0].mesh.indices.size(); VertexArray vertices = wapp_array_alloc_capacity(Vertex, &arena, 128, ARRAY_INIT_NONE); U16Array indices = wapp_array_alloc_capacity(u16, &arena, 128, ARRAY_INIT_NONE); for (tinyobj::index_t &index : shapes[0].mesh.indices) { Vertex v = {}; v.pos = { attrib.vertices[index.vertex_index * 3], -attrib.vertices[index.vertex_index * 3 + 1], attrib.vertices[index.vertex_index * 3 + 2] }; v.normal = { attrib.normals[index.normal_index * 3], -attrib.normals[index.normal_index * 3 + 1], attrib.normals[index.normal_index * 3 + 2] }; v.uv = { attrib.texcoords[index.texcoord_index * 2], 1.0 - attrib.texcoords[index.texcoord_index * 2 + 1] }; u16 idx = (u16)wapp_array_count(indices); wapp_array_append_alloc(Vertex, &arena, vertices, &v, ARRAY_INIT_NONE); wapp_array_append_alloc(u16, &arena, indices, &idx, ARRAY_INIT_NONE); } // }}} // {{{ Upload to GPU VkDeviceSize vertex_buf_size = sizeof(Vertex) * wapp_array_count(vertices); VkDeviceSize index_buf_size = sizeof(u16) * wapp_array_count(vertices); VkBufferCreateInfo buf_create_info = {}; buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buf_create_info.size = vertex_buf_size + index_buf_size; buf_create_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT; VmaAllocationCreateInfo buf_alloc_create_info = {}; buf_alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; buf_alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO; VmaAllocationInfo buf_alloc_info = {}; check(vmaCreateBuffer(allocator, &buf_create_info, &buf_alloc_create_info, &vert_index_buf, &vert_index_buf_alloc, &buf_alloc_info)); memcpy(buf_alloc_info.pMappedData, vertices, vertex_buf_size); memcpy((void *)((u8 *)buf_alloc_info.pMappedData + vertex_buf_size), indices, index_buf_size); // }}} // }}} wapp_mem_arena_allocator_temp_end(&arena); // {{{ Shader Data Buffers shader_data_bufs = wapp_array_with_capacity(ShaderDataBuffer, 3, ARRAY_INIT_FILLED); for (u32 i = 0; i < max_frames_in_flight; ++i) { // {{{ Create Buffer VkBufferUsageFlags2CreateInfo data_buf_usage_flags = {}; data_buf_usage_flags.sType = VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO; data_buf_usage_flags.usage = VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT; VkBufferCreateInfo data_buf_create_info = {}; data_buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; data_buf_create_info.size = sizeof(ShaderData); data_buf_create_info.usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; data_buf_create_info.pNext = &data_buf_usage_flags; VmaAllocationCreateInfo data_buf_alloc_create_info = {}; data_buf_alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; data_buf_create_info.usage = VMA_MEMORY_USAGE_AUTO; check(vmaCreateBuffer(allocator, &data_buf_create_info, &data_buf_alloc_create_info, &shader_data_bufs[i].buffer, &shader_data_bufs[i].allocation, &shader_data_bufs[i].allocation_info)); // }}} // {{{ Get Buffer GPU Address VkBufferDeviceAddressInfo buf_dev_addr_info = {}; buf_dev_addr_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; buf_dev_addr_info.buffer = shader_data_bufs[i].buffer; shader_data_bufs[i].device_address = vkGetBufferDeviceAddress(device, &buf_dev_addr_info); // }}} } // }}} // {{{ Render Loop SDL_Event event = {}; while (running) { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_EVENT_QUIT: running = false; break; case SDL_EVENT_KEY_DOWN: if (event.key.key == SDLK_ESCAPE) { running = false; } break; } } } // }}} // {{{ Cleanup for (u32 i = 0; i < max_frames_in_flight; ++i) { vmaDestroyBuffer(allocator, shader_data_bufs[i].buffer, shader_data_bufs[i].allocation); } vmaDestroyBuffer(allocator, vert_index_buf, vert_index_buf_alloc); vkDestroyImageView(device, depth_view, nullptr); vmaDestroyImage(allocator, depth_image, depth_allocation); for (u32 i = 0; i < swapchain_image_count; ++i) { vkDestroyImageView(device, swapchain_views[i], nullptr); } vkDestroySwapchainKHR(device, swapchain, nullptr); vmaDestroyAllocator(allocator); vkDestroyDevice(device, nullptr); vkDestroySurfaceKHR(instance, surface, nullptr); vkDestroyInstance(instance, nullptr); SDL_DestroyWindow(window); SDL_Vulkan_UnloadLibrary(); SDL_Quit(); wapp_mem_arena_allocator_destroy(&arena); // }}} return 0; } // {{{ Helper Functions wapp_intern inline void check(VkResult result) { if (result != VK_SUCCESS) { std::cerr << "Vulkan call returned an error (" << string_VkResult(result) << ")\n"; exit(result); } } wapp_intern inline void check_swapchain(VkResult result) { if (result < VK_SUCCESS) { if (result == VK_ERROR_OUT_OF_DATE_KHR) { update_swapchain = true; return; } std::cerr << "Vulkan call returned an error (" << result << ")\n"; exit(result); } } wapp_intern inline void check(bool result, i32 code) { if (!result) { std::cerr << "Call returned an error\n"; exit(code); } } // }}}