/** * BACK END API */ // TODO(MA): Make separate functions for debug/non debug builds for readability b32 _InitRenderer(Arena *arena) { Assert(InitVulkan(arena), "InitVkLib failure"); return true; } void _DestroyRenderer() { DestroyVulkan(); } static u32 GetFrameIndex() { return renderer.frame_state.frame_cnt % renderer.vk.sc.img_count; } static VkCommandBuffer GetFrameCmdBuf() { return renderer.vk.frame.buffers[GetFrameIndex()]; } static VkFence *GetFrameRenderFence() { return &renderer.vk.frame.render_fences[GetFrameIndex()]; } static VkSemaphore GetFrameRenderSem() { return renderer.vk.frame.render_sems[GetFrameIndex()]; } static VkSemaphore GetFrameSwapSem() { return renderer.vk.frame.swapchain_sems[GetFrameIndex()]; } b32 _BeginFrame() { b32 success = true; VkResult result; VkDevice device = renderer.vk.device; VkSwapchainKHR swapchain = renderer.vk.swapchain; VkCommandBuffer cmd = GetFrameCmdBuf(); VkFence *fence = GetFrameRenderFence(); VkSemaphore sc_sem = GetFrameSwapSem(); VkSemaphore rndr_sem = GetFrameRenderSem(); // TODO(MA): make this work with VK_PRESENT_MODE_MAILBOX_KHR and remove assignment of present mode to FIFO result = vkWaitForFences(device, 1, fence, VK_TRUE, 1000000000); if (result != VK_SUCCESS) { Printf("vkWaitForFences failure: %s", VkResultStr(result)); success = false; } if (success) { result = vkResetFences(device, 1, fence); if (result != VK_SUCCESS) { Printf("vkResetFences failure: %s", VkResultStr(result)); success = false; } } if (success) { result = vkAcquireNextImageKHR(device, swapchain, 1000000000, sc_sem, 0, &renderer.frame_state.img_ix); if (result == VK_ERROR_OUT_OF_DATE_KHR) ResizeSwapchain(); else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { Printf("vkAcquireNextImageKHR failure: %s", VkResultStr(result)); success = false; } } if (success) { result = vkResetCommandBuffer(cmd, 0); if (result != VK_SUCCESS) { Printf("vkResetCommandBuffer failure: %s", VkResultStr(result)); success = false; } } if (success) { VkCommandBufferBeginInfo cmd_info = { .sType = STYPE(COMMAND_BUFFER_BEGIN_INFO), .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; result = vkBeginCommandBuffer(cmd, &cmd_info); if (result != VK_SUCCESS) { Printf("vkBeginCommandBuffer failure: %s", VkResultStr(result)); success = false; } } if (success) { TransitionImage(cmd, &renderer.vk.sc.draw_img, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); TransitionImage(cmd, &renderer.vk.sc.depth_img, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); VkRenderingAttachmentInfo col_attach_info = { .sType = STYPE(RENDERING_ATTACHMENT_INFO), .imageView = renderer.vk.sc.draw_img.view, .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, }; VkRenderingAttachmentInfo depth_attach_info = { .sType = STYPE(RENDERING_ATTACHMENT_INFO), .imageView = renderer.vk.sc.depth_img.view, .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, }; VkRenderingInfo render_info = { .sType = STYPE(RENDERING_INFO), .layerCount = 1, .colorAttachmentCount = 1, .pColorAttachments = &col_attach_info, .pDepthAttachment = &depth_attach_info, .renderArea = { .extent = { .width = renderer.vk.sc.extent.width, .height = renderer.vk.sc.extent.height, }, }, }; vkCmdBeginRendering(cmd, &render_info); } return success; } void _ClearScreen() { } void _DrawTriangle() { VkCommandBuffer cmd = GetFrameCmdBuf(); vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer.vk.pipe.pipelines[PIPELINE_CUBE]); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer.vk.pipe.pipeline_layout, 0, DESC_TYPE_MAX, renderer.vk.pipe.sets, 0, NULL); VkViewport viewport = { .width = (f32)renderer.vk.sc.extent.width, .height = (f32)renderer.vk.sc.extent.height, .maxDepth = 1.0, }; vkCmdSetViewport(cmd, 0, 1, &viewport); VkRect2D scissor = { .extent = { .width = renderer.vk.sc.extent.width, .height = renderer.vk.sc.extent.height, }, }; vkCmdSetScissor(cmd, 0, 1, &scissor); vkCmdDraw(cmd, 3, 1, 0, 0); // TODO(MA): maybe store current image layout between functions } b32 _FinishFrame() { b32 success = true; VkResult result; VkCommandBuffer cmd = GetFrameCmdBuf(); VkSemaphore sc_sem = GetFrameSwapSem(); VkSemaphore rndr_sem = GetFrameRenderSem(); VkFence *fence = GetFrameRenderFence(); VkImage curr_img = renderer.vk.sc.imgs[renderer.frame_state.img_ix]; vkCmdEndRendering(cmd); TransitionImage(cmd, &renderer.vk.sc.draw_img, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); TransitionImageLayout(cmd, curr_img, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkExtent2D extent = { .width = renderer.vk.sc.extent.width, .height = renderer.vk.sc.extent.height, }; CopyImageToImage(cmd, renderer.vk.sc.draw_img.img, curr_img, extent, extent); TransitionImageLayout(cmd, curr_img, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); result = vkEndCommandBuffer(cmd); if (result != VK_SUCCESS) { Printf("vkEndCommandBuffer failure: %s", VkResultStr(result)); success = false; } if (success) { VkCommandBufferSubmitInfo cmd_info = { .sType = STYPE(COMMAND_BUFFER_SUBMIT_INFO), .commandBuffer = cmd, }; VkSemaphoreSubmitInfo wait_info = { .sType = STYPE(SEMAPHORE_SUBMIT_INFO), .semaphore = sc_sem, .stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, .value = 1, }; VkSemaphoreSubmitInfo signal_info = { .sType = STYPE(SEMAPHORE_SUBMIT_INFO), .semaphore = rndr_sem, .stageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, .value = 1, }; VkSubmitInfo2 submit_info = { .sType = STYPE(SUBMIT_INFO_2), .waitSemaphoreInfoCount = 1, .pWaitSemaphoreInfos = &wait_info, .signalSemaphoreInfoCount = 1, .pSignalSemaphoreInfos = &signal_info, .commandBufferInfoCount = 1, .pCommandBufferInfos = &cmd_info, }; result = vkQueueSubmit2(renderer.vk.queues.graphics_queue, 1, &submit_info, *fence); if (result != VK_SUCCESS) { Printf("vkQueueSubmit2 failure: %s", VkResultStr(result)); success = false; } } if (success) { VkPresentInfoKHR present_info = { .sType = STYPE(PRESENT_INFO_KHR), .pSwapchains = &renderer.vk.swapchain, .swapchainCount = 1, .pWaitSemaphores = &rndr_sem, .waitSemaphoreCount = 1, .pImageIndices = &renderer.frame_state.img_ix, }; result = vkQueuePresentKHR(renderer.vk.queues.graphics_queue, &present_info); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || renderer.resize_requested) { renderer.resize_requested = false; ResizeSwapchain(); } else if (result != VK_SUCCESS) { Printf("vkQueuePresentKHR failure: %s", VkResultStr(result)); success = false; } } renderer.frame_state.frame_cnt++; return success; } static void _SetRenderResolution(u32 x, u32 y) { renderer.vk.sc.extent.width = x; renderer.vk.sc.extent.height = y; renderer.resize_requested = true; } /** * BACK END API END */ /** * INTERNAL API */ // Swapchain static void ResizeSwapchain() { vkDeviceWaitIdle(renderer.vk.device); DestroySwapchain(); DestroyDrawImages(); Assert(CreateSwapchain(), "Unable to recreate swapchain"); Assert(CreateDrawImages(), "Unable to recreate draw images"); renderer.resize_requested = false; } // UTIL static void CopyImageToImage(VkCommandBuffer cmd, VkImage src, VkImage dst, VkExtent2D src_ext, VkExtent2D dst_ext) { VkImageBlit2 blit = { .sType = STYPE(IMAGE_BLIT_2), .srcOffsets = { {}, { .x = (i32)src_ext.width, .y = (i32)src_ext.height, .z = 1 }, }, .dstOffsets = { {}, { .x = (i32)dst_ext.width, .y = (i32)dst_ext.height, .z = 1 }, }, .srcSubresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseArrayLayer = 0, .layerCount = 1, .mipLevel = 0, }, .dstSubresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseArrayLayer = 0, .layerCount = 1, .mipLevel = 0, }, }; VkBlitImageInfo2 blit_info = { .sType = STYPE(BLIT_IMAGE_INFO_2), .srcImage = src, .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .dstImage = dst, .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .filter = VK_FILTER_LINEAR, .regionCount = 1, .pRegions = &blit, }; vkCmdBlitImage2(cmd, &blit_info); } static void TransitionImageLayout(VkCommandBuffer cmd, VkImage img, VkImageLayout curr, VkImageLayout new) { VkImageMemoryBarrier2 barrier = { .sType = STYPE(IMAGE_MEMORY_BARRIER_2), .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, .dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, .dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT, .oldLayout = curr, .newLayout = new, .image = img, .subresourceRange = { .aspectMask = new == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; VkDependencyInfo dep_info = { .sType = STYPE(DEPENDENCY_INFO), .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &barrier, }; vkCmdPipelineBarrier2(cmd, &dep_info); } static void TransitionImage(VkCommandBuffer cmd, Image *img, VkImageLayout new) { TransitionImageLayout(cmd, img->img, img->curr_layout, new); img->curr_layout = new; } // INIT static b32 InitVulkan(Arena *arena) { Assert(arena != NULL, "Vulkan memory is null"); renderer.perm_arena = arena; void *mem = ArenaAlloc(arena, MB(4)); renderer.arena = CreateArena(mem, MB(4)); Assert(InitVkGlobalFunctions(), "Unable to load vulkan functions"); Assert(vkCreateInstance(&inst_info, NULL, &renderer.vk.inst) == VK_SUCCESS, "Error initializing instance"); Assert(InitVkInstanceFunctions(), "Unable to initialize instance functions"); #ifdef BUILD_DEBUG { Assert(VLayersSupported(), "DEBUG_BUILD ERROR: Validation layers are not supported"); Assert(vkCreateDebugUtilsMessengerEXT(renderer.vk.inst, &debug_msg_info, NULL, &renderer.vk.debug) == VK_SUCCESS, "Unable to initialize debug messenger"); } #endif Assert(CreateSurface(), "Unable to create surface"); Assert(CreateDevice(), "Unable to create device"); Assert(CreateVmaAllocator(), "Unable to create VMA allocator"); Assert(CreateSwapchain(), "Unable to initialize swapchain and draw images"); Assert(CreateDrawImages(), "Unable to create draw images"); Assert(CreateFrameStructures(), "Unable to create frame structures"); Assert(CreateImmediateStructures(), "Unable to create immediate structures"); Assert(CreateDescriptors(), "Unable to initialize descriptors."); Assert(CreatePipelines(), "Unable to initialize pipelines."); ArenaFree(renderer.arena); return true; } static b32 CreateVmaAllocator() { VmaVulkanFunctions vk_functions = { .vkGetInstanceProcAddr = vkGetInstanceProcAddr, .vkGetDeviceProcAddr = vkGetDeviceProcAddr, }; vma_create_info.pVulkanFunctions = &vk_functions; vma_create_info.physicalDevice = renderer.vk.phys_device; vma_create_info.device = renderer.vk.device; vma_create_info.instance = renderer.vk.inst; VkResult result = vmaCreateAllocator(&vma_create_info, &renderer.vk.alloc); if (result != VK_SUCCESS) { Printf("vmaCreateAllocator failure: %d", result); } return result == VK_SUCCESS; } static b32 CheckQueueSurfaceSupport(i32 index, VkPhysicalDevice device, VkSurfaceKHR surface) { b32 surface_supported; vkGetPhysicalDeviceSurfaceSupportKHR(device, (u32)index, surface, &surface_supported); return surface_supported; } static DeviceQueues CheckDeviceQueueSupport(VkPhysicalDevice device, VkSurfaceKHR surface) { DeviceQueues queues = { .graphics = -1, .transfer = -1 }; u32 queue_count; vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_count, NULL); VkQueueFamilyProperties *families = ArenaAlloc(renderer.arena, sizeof(VkQueueFamilyProperties) * queue_count); vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_count, families); for (i32 i = 0; i < queue_count; i++) { if (i == 0 && CheckQueueSurfaceSupport(i, device, surface)) { queues.graphics = i; continue; } if (BitEq(families[i].queueFlags, VK_QUEUE_TRANSFER_BIT)) queues.transfer = i; } if (queues.transfer < 0) queues.transfer = queues.graphics; return queues; } static b32 CheckDevicePropertiesSupport(VkPhysicalDevice device, VkSurfaceKHR surface, b32 *discrete) { b32 success = false; VkPhysicalDeviceProperties properties = {}; vkGetPhysicalDeviceProperties(device, &properties); if (VK_API_VERSION_MINOR(properties.apiVersion) >= 3) { u32 ext_count; vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, NULL); VkExtensionProperties *ext_properties = ArenaAlloc(renderer.arena, sizeof(VkExtensionProperties) * ext_count); vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, ext_properties); i32 matched = 0; for (u32 i = 0; i < ext_count; i++) { for (i32 j = 0; j < Len(device_extensions); j++) { if (StrEq(ext_properties[i].extensionName, device_extensions[j])) { matched++; break; } } } if (matched == Len(device_extensions)) { u32 fmt_count, present_count; vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &fmt_count, NULL); vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, NULL); *discrete = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU; success = fmt_count && present_count; } } return success; } static b32 CheckDeviceFeatureSupport(VkPhysicalDevice device) { VkPhysicalDeviceFeatures2 features2 = { .sType = STYPE(PHYSICAL_DEVICE_FEATURES_2) }; VkPhysicalDeviceVulkan12Features features_12 = { .sType = STYPE(PHYSICAL_DEVICE_VULKAN_1_2_FEATURES) }; VkPhysicalDeviceVulkan13Features features_13 = { .sType = STYPE(PHYSICAL_DEVICE_VULKAN_1_3_FEATURES) }; features2.pNext = &features_12; vkGetPhysicalDeviceFeatures2(device, &features2); features2.pNext = &features_13; vkGetPhysicalDeviceFeatures2(device, &features2); VkPhysicalDeviceFeatures features = features2.features; b32 result = true; result &= (b32)features.shaderUniformBufferArrayDynamicIndexing; result &= (b32)features.shaderSampledImageArrayDynamicIndexing; result &= (b32)features.shaderStorageBufferArrayDynamicIndexing; result &= (b32)features.shaderStorageImageArrayDynamicIndexing; result &= (b32)features_12.descriptorIndexing; result &= (b32)features_12.bufferDeviceAddress; result &= (b32)features_13.synchronization2; result &= (b32)features_13.dynamicRendering; return result; } static b32 CreateDevice() { VkInstance inst = renderer.vk.inst; VkSurfaceKHR surface = renderer.vk.surface; u32 device_count; vkEnumeratePhysicalDevices(inst, &device_count, NULL); VkPhysicalDevice *devices = ArenaAlloc(renderer.arena, sizeof(VkPhysicalDevice) * device_count); vkEnumeratePhysicalDevices(inst, &device_count, devices); b32 discrete_device = false; DeviceQueues *queues = &renderer.vk.queues; VkPhysicalDevice *phys_device = &renderer.vk.phys_device; for (u32 i = 0; i < device_count; i++) { DeviceQueues current_queues = CheckDeviceQueueSupport(devices[i], surface); b32 discrete = false; if (current_queues.graphics < 0) continue; if (!CheckDevicePropertiesSupport(devices[i], surface, &discrete)) continue; if (discrete_device && !discrete) continue; if (!CheckDeviceFeatureSupport(devices[i])) continue; discrete_device = discrete; *queues = current_queues; *phys_device = devices[i]; if (discrete_device && queues->graphics != queues->transfer) break; } b32 success = false; if (phys_device != NULL) { VkDeviceQueueCreateInfo queue_info[2] = {}; f32 priority = 1.0f; u32 count = 1; queue_info[0].sType = STYPE(DEVICE_QUEUE_CREATE_INFO); queue_info[0].queueFamilyIndex = queues->graphics; queue_info[0].queueCount = 1; queue_info[0].pQueuePriorities = &priority; queue_info[0].flags = 0; if (queues->graphics != queues->transfer) { queue_info[1].sType = STYPE(DEVICE_QUEUE_CREATE_INFO); queue_info[1].queueFamilyIndex = queues->transfer; queue_info[1].queueCount = 1; queue_info[1].pQueuePriorities = &priority; queue_info[1].flags = 0; count++; } device_info.queueCreateInfoCount = count; device_info.pQueueCreateInfos = &queue_info[0]; VkResult result = vkCreateDevice(renderer.vk.phys_device, &device_info, NULL, &renderer.vk.device); if (result != VK_SUCCESS) { Printf("vkCreateDevice failure: %d", result); } else { Assert(InitVkDeviceFunctions(), "Failed to initialize device functions"); vkGetDeviceQueue(renderer.vk.device, queues->graphics, 0, &queues->graphics_queue); vkGetDeviceQueue(renderer.vk.device, queues->transfer, 0, &queues->transfer_queue); success = true; } } return success; } static b32 InitVkGlobalFunctions() { b32 result = LoadVulkanLib(); if (result) { INIT_FN(vkGetInstanceProcAddr); INIT_FN(vkEnumerateInstanceLayerProperties); INIT_FN(vkCreateInstance); } return result; } static b32 InitVkInstanceFunctions() { VkInstance instance = renderer.vk.inst; #ifdef __linux__ { INIT_INST_FN(vkCreateXcbSurfaceKHR); } #endif #ifdef BUILD_DEBUG { INIT_INST_FN(vkCreateDebugUtilsMessengerEXT); INIT_INST_FN(vkDestroyDebugUtilsMessengerEXT); } #endif INIT_INST_FN(vkEnumeratePhysicalDevices); INIT_INST_FN(vkGetPhysicalDeviceQueueFamilyProperties); INIT_INST_FN(vkGetPhysicalDeviceSurfaceSupportKHR); INIT_INST_FN(vkCreateDevice); INIT_INST_FN(vkGetPhysicalDeviceProperties); INIT_INST_FN(vkGetPhysicalDeviceFeatures2); INIT_INST_FN(vkGetPhysicalDeviceSurfaceFormatsKHR); INIT_INST_FN(vkGetPhysicalDeviceSurfacePresentModesKHR); INIT_INST_FN(vkEnumerateDeviceExtensionProperties); INIT_INST_FN(vkGetDeviceProcAddr); INIT_INST_FN(vkDestroyInstance); INIT_INST_FN(vkDestroySurfaceKHR); INIT_INST_FN(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); INIT_INST_FN(vkGetPhysicalDeviceImageFormatProperties); return true; } static b32 InitVkDeviceFunctions() { VkDevice device = renderer.vk.device; INIT_DEV_FN(vkCreateSwapchainKHR); INIT_DEV_FN(vkCreateImage); INIT_DEV_FN(vkCreateImageView); INIT_DEV_FN(vkGetSwapchainImagesKHR); INIT_DEV_FN(vkGetDeviceQueue); INIT_DEV_FN(vkCreateSemaphore); INIT_DEV_FN(vkAllocateCommandBuffers); INIT_DEV_FN(vkCreateCommandPool); INIT_DEV_FN(vkCreateFence); INIT_DEV_FN(vkCreateDescriptorPool); INIT_DEV_FN(vkCreateDescriptorSetLayout); INIT_DEV_FN(vkAllocateDescriptorSets); INIT_DEV_FN(vkCreatePipelineLayout); INIT_DEV_FN(vkResetDescriptorPool); INIT_DEV_FN(vkCreateShaderModule); INIT_DEV_FN(vkCreateGraphicsPipelines); INIT_DEV_FN(vkCreateComputePipelines); INIT_DEV_FN(vkUpdateDescriptorSets); INIT_DEV_FN(vkDestroyDevice); INIT_DEV_FN(vkDestroyDescriptorPool); INIT_DEV_FN(vkDestroySwapchainKHR); INIT_DEV_FN(vkDestroyImage); INIT_DEV_FN(vkDestroyImageView); INIT_DEV_FN(vkDestroyCommandPool); INIT_DEV_FN(vkDestroySemaphore); INIT_DEV_FN(vkDestroyFence); INIT_DEV_FN(vkDestroyPipelineLayout); INIT_DEV_FN(vkDestroyPipeline); INIT_DEV_FN(vkWaitForFences); INIT_DEV_FN(vkBeginCommandBuffer); INIT_DEV_FN(vkEndCommandBuffer); INIT_DEV_FN(vkAcquireNextImageKHR); INIT_DEV_FN(vkCmdBindPipeline); INIT_DEV_FN(vkCmdBindDescriptorSets); INIT_DEV_FN(vkCmdDispatch); INIT_DEV_FN(vkCmdBeginRendering); INIT_DEV_FN(vkCmdEndRendering); INIT_DEV_FN(vkCmdSetViewport); INIT_DEV_FN(vkCmdSetScissor); INIT_DEV_FN(vkCmdPushConstants); INIT_DEV_FN(vkCmdBindIndexBuffer); INIT_DEV_FN(vkCmdDrawIndexed); INIT_DEV_FN(vkCmdBlitImage2); INIT_DEV_FN(vkCmdPipelineBarrier2); INIT_DEV_FN(vkCmdCopyBufferToImage); INIT_DEV_FN(vkCmdCopyBuffer); INIT_DEV_FN(vkQueueSubmit2); INIT_DEV_FN(vkResetFences); INIT_DEV_FN(vkResetCommandBuffer); INIT_DEV_FN(vkFreeCommandBuffers); INIT_DEV_FN(vkDestroyDescriptorSetLayout); INIT_DEV_FN(vkDestroyShaderModule); INIT_DEV_FN(vkQueuePresentKHR); INIT_DEV_FN(vkCmdDraw); INIT_DEV_FN(vkDeviceWaitIdle); return true; } // TODO(MA): implement other platforms #ifdef __linux__ static b32 CreateSurface() { Window *window = GetWindow(); VkXcbSurfaceCreateInfoKHR surface_info = { .sType = STYPE(XCB_SURFACE_CREATE_INFO_KHR), .connection = window->connection, .window = window->window }; VkResult result = vkCreateXcbSurfaceKHR(renderer.vk.inst, &surface_info, NULL, &renderer.vk.surface); if (result != VK_SUCCESS) { Printf("Unable to create surface: %d", result); } return result == VK_SUCCESS; } #endif static b32 LoadVulkanLib() { Library *lib = &renderer.vk.lib; b32 lib_found; Function fn; for (i32 i = 0; i < Len(vulkan_libs); i++) { lib_found = LoadLib(vulkan_libs[i], lib); if (lib_found) { lib_found = LoadFn("vkGetInstanceProcAddr", lib, &fn); vkGetInstanceProcAddr = fn.fn; break; } } return lib_found; } static b32 VLayersSupported() { b32 success; u32 count; vkEnumerateInstanceLayerProperties(&count, NULL); Assert(count, "VLayersSupported(): vkEnumerateInstanceLayerProperties returned a count of 0"); VkLayerProperties *layers = ArenaAlloc(renderer.arena, sizeof(VkLayerProperties) * count); vkEnumerateInstanceLayerProperties(&count, layers); for (u32 i = 0; i < count; i++) { if (StrEq(layers[i].layerName, _VK_VALIDATION)) { success = true; break; } } return success; } static b32 CreateFrameStructures() { b32 success = true; FrameStructures *data = &renderer.vk.frame; u32 img_count = renderer.vk.sc.img_count; pool_create_info.queueFamilyIndex = renderer.vk.queues.graphics; renderer.vk.frame.pools = ArenaAlloc(renderer.perm_arena, sizeof(VkCommandPool) * img_count); renderer.vk.frame.buffers = ArenaAlloc(renderer.perm_arena, sizeof(VkCommandBuffer) * img_count); renderer.vk.frame.swapchain_sems = ArenaAlloc(renderer.perm_arena, sizeof(VkSemaphore) * img_count); renderer.vk.frame.render_sems = ArenaAlloc(renderer.perm_arena, sizeof(VkSemaphore) * img_count); renderer.vk.frame.render_fences = ArenaAlloc(renderer.perm_arena, sizeof(VkFence) * img_count); for (u32 i = 0; i < renderer.vk.sc.img_count; i++) { VkResult result; VkDevice device = renderer.vk.device; result = vkCreateCommandPool(device, &pool_create_info, NULL, &data->pools[i]); if (result != VK_SUCCESS) success = false; cmd_buf_info.commandPool = data->pools[i]; result = vkAllocateCommandBuffers(device, &cmd_buf_info, &data->buffers[i]); if (result != VK_SUCCESS) success = false; result = vkCreateFence(device, &fence_create_info, NULL, &data->render_fences[i]); if (result != VK_SUCCESS) success = false; result = vkCreateSemaphore(device, &semaphore_create_info, NULL, &data->render_sems[i]); if (result != VK_SUCCESS) success = false; result = vkCreateSemaphore(device, &semaphore_create_info, NULL, &data->swapchain_sems[i]); if (result != VK_SUCCESS) success = false; } return success; } static b32 CreateImmediateStructures() { b32 success = true; VkResult result; VkDevice device = renderer.vk.device; ImmediateStructures *imm = &renderer.vk.imm; pool_create_info.queueFamilyIndex = renderer.vk.queues.transfer; result = vkCreateCommandPool(device, &pool_create_info, NULL, &imm->pool); if (result != VK_SUCCESS) success = false; cmd_buf_info.commandPool = imm->pool; result = vkAllocateCommandBuffers(device, &cmd_buf_info, &imm->buffer); if (result != VK_SUCCESS) success = false; result = vkCreateFence(device, &fence_create_info, NULL, &imm->fence); if (result != VK_SUCCESS) success = false; return success; } static b32 CreateSwapchain() { b32 success = true; VkPhysicalDevice phys_device = renderer.vk.phys_device; VkDevice device = renderer.vk.device; VkSurfaceKHR surface = renderer.vk.surface; VkSurfaceCapabilitiesKHR capabilities; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_device, surface, &capabilities); // Maybe reconsider handling window sizing within here and only handle it from events themselves // causes issues when the window size is out of sync with the current swapchain VkExtent2D extent; u32 width = renderer.vk.sc.extent.width; u32 height = renderer.vk.sc.extent.height; extent.width = u32Clamp((u32)width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); extent.height = u32Clamp((u32)height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); u32 format_count; vkGetPhysicalDeviceSurfaceFormatsKHR(phys_device, surface, &format_count, NULL); VkSurfaceFormatKHR *formats = ArenaAlloc(renderer.arena, sizeof(VkSurfaceFormatKHR) * format_count); vkGetPhysicalDeviceSurfaceFormatsKHR(phys_device, surface, &format_count, formats); renderer.vk.sc.format = formats[0].format; renderer.vk.sc.color_space = formats[0].colorSpace; u32 present_count; vkGetPhysicalDeviceSurfacePresentModesKHR(phys_device, surface, &present_count, NULL); VkPresentModeKHR *present_modes = ArenaAlloc(renderer.arena, sizeof(VkSurfaceFormatKHR) * present_count); vkGetPhysicalDeviceSurfacePresentModesKHR(phys_device, surface, &present_count, present_modes); VkPresentModeKHR present_mode; for (u32 i = 0; i < present_count; i++) { if (present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { present_mode = VK_PRESENT_MODE_MAILBOX_KHR; break; } } if (present_mode != VK_PRESENT_MODE_MAILBOX_KHR) present_mode = VK_PRESENT_MODE_FIFO_KHR; swapchain_create_info.minImageCount = capabilities.minImageCount + 1; swapchain_create_info.surface = surface; swapchain_create_info.imageFormat = renderer.vk.sc.format; swapchain_create_info.imageColorSpace = renderer.vk.sc.color_space; swapchain_create_info.imageExtent = extent; swapchain_create_info.preTransform = capabilities.currentTransform; swapchain_create_info.presentMode = present_mode; VkResult result; result = vkCreateSwapchainKHR(device, &swapchain_create_info, NULL, &renderer.vk.swapchain); if (result != VK_SUCCESS) success = false; u32 image_count; vkGetSwapchainImagesKHR(device, renderer.vk.swapchain, &image_count, NULL); VkImage *sc_images = ArenaAlloc(renderer.perm_arena, sizeof(VkImage) * image_count); VkImageView *sc_views = ArenaAlloc(renderer.perm_arena, sizeof(VkImageView) * image_count); vkGetSwapchainImagesKHR(device, renderer.vk.swapchain, &image_count, sc_images); for (u32 i = 0; i < image_count; i++) { sc_image_view_create_info.image = sc_images[i]; sc_image_view_create_info.format = renderer.vk.sc.format; result = vkCreateImageView(device, &sc_image_view_create_info, NULL, &sc_views[i]); if (result != VK_SUCCESS) success = false; } renderer.vk.sc.imgs = sc_images; renderer.vk.sc.views = sc_views; renderer.vk.sc.img_count = image_count; renderer.vk.sc.extent.width = extent.width; renderer.vk.sc.extent.height = extent.height; renderer.vk.sc.extent.depth = 1; return success; } static b32 CreateDrawImages() { b32 success = true; VkResult result; VkFormat image_format = GetImageFormat(); VkExtent3D extent = renderer.vk.sc.extent; VkDevice device = renderer.vk.device; VmaAllocationCreateInfo alloc_create_info = { .usage = VMA_MEMORY_USAGE_GPU_ONLY, .requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, }; // Draw Image draw_image_create_info.format = image_format; draw_image_create_info.extent = extent; result = vmaCreateImage(renderer.vk.alloc, &draw_image_create_info, &alloc_create_info, &renderer.vk.sc.draw_img.img, &renderer.vk.sc.draw_img.alloc, NULL); if (result != VK_SUCCESS) success = false; // Draw Image View draw_image_view_create_info.image = renderer.vk.sc.draw_img.img; draw_image_view_create_info.format = image_format; result = vkCreateImageView(device, &draw_image_view_create_info, NULL, &renderer.vk.sc.draw_img.view); if (result != VK_SUCCESS) success = false; // Depth Image depth_image_create_info.extent = extent; result = vmaCreateImage(renderer.vk.alloc, &depth_image_create_info, &alloc_create_info, &renderer.vk.sc.depth_img.img, &renderer.vk.sc.depth_img.alloc, NULL); if (result != VK_SUCCESS) success = false; // Depth Image View depth_image_view_create_info.image = renderer.vk.sc.depth_img.img; result = vkCreateImageView(device, &depth_image_view_create_info, NULL, &renderer.vk.sc.depth_img.view); if (result != VK_SUCCESS) success = false; // Setting values renderer.vk.sc.extent = extent; renderer.vk.sc.depth_img.fmt = depth_image_create_info.format; renderer.vk.sc.depth_img.curr_layout = VK_IMAGE_LAYOUT_UNDEFINED; renderer.vk.sc.draw_img.fmt = draw_image_create_info.format; renderer.vk.sc.draw_img.curr_layout = VK_IMAGE_LAYOUT_UNDEFINED; return success; } static VkFormat GetImageFormat() { VkPhysicalDevice phys_device = renderer.vk.phys_device; VkImageType image_type = VK_IMAGE_TYPE_2D; VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; VkFormat format = 0; for (i32 i = 0; i < Len(VK_IMAGE_FORMATS); i++) { VkImageFormatProperties properties; VkResult result; result = vkGetPhysicalDeviceImageFormatProperties(phys_device, VK_IMAGE_FORMATS[i], image_type, tiling, usage_flags, 0, &properties); if (result == VK_ERROR_FORMAT_NOT_SUPPORTED) continue; if (result == VK_SUCCESS) { format = VK_IMAGE_FORMATS[i]; break; } } Assert(format != 0, "[Error] unable to find appropriate image format"); return format; } static b32 CreateDescriptors() { b32 success = true; VkDevice device = renderer.vk.device; VkResult result; result = vkCreateDescriptorPool(device, &desc_pool_info, NULL, &renderer.vk.pipe.pool); if (result != VK_SUCCESS) success = false; result = vkCreateDescriptorSetLayout(device, &shared_layout_create_info, NULL, &renderer.vk.pipe.layouts[DESC_TYPE_SHARED]); if (result != VK_SUCCESS) success = false; for (u32 i = DESC_TYPE_COMBINED_SAMPLER; i < DESC_TYPE_MAX; i++) { bindless_layout_binding.descriptorType = desc_type_map[i]; result = vkCreateDescriptorSetLayout(device, &bindless_layout_create_info, NULL, &renderer.vk.pipe.layouts[i]); if (result != VK_SUCCESS) success = false; } set_allocate_info.descriptorPool = renderer.vk.pipe.pool; set_allocate_info.pSetLayouts = renderer.vk.pipe.layouts; result = vkAllocateDescriptorSets(device, &set_allocate_info, renderer.vk.pipe.sets); if (result != VK_SUCCESS) success = false; pipeline_layout_create_info.setLayoutCount = DESC_TYPE_MAX; pipeline_layout_create_info.pSetLayouts = renderer.vk.pipe.layouts; result = vkCreatePipelineLayout(device, &pipeline_layout_create_info, NULL, &renderer.vk.pipe.pipeline_layout); if (result != VK_SUCCESS) success = false; return success; } static b32 CreatePipelines() { b32 success = true; VkResult result; VkDevice device = renderer.vk.device; VkShaderModule cube_vert, cube_frag; success &= CreateShaderModule(shader_quad_vert, shader_quad_vert_len, &cube_vert); success &= CreateShaderModule(shader_quad_frag, shader_quad_frag_len, &cube_frag); VkPipelineShaderStageCreateInfo cube_shader_stages[] = { { .sType = STYPE(PIPELINE_SHADER_STAGE_CREATE_INFO), .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = cube_vert, .pName = "main", }, { .sType = STYPE(PIPELINE_SHADER_STAGE_CREATE_INFO), .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = cube_frag, .pName = "main", }, }; VkPipelineRenderingCreateInfo pipeline_render_info = { .sType = STYPE(PIPELINE_RENDERING_CREATE_INFO), .colorAttachmentCount = 1, .pColorAttachmentFormats = &renderer.vk.sc.draw_img.fmt, .depthAttachmentFormat = renderer.vk.sc.depth_img.fmt, }; cube_create_info.pStages = cube_shader_stages; cube_create_info.stageCount = Len(cube_shader_stages); cube_create_info.layout = renderer.vk.pipe.pipeline_layout; cube_create_info.pNext = &pipeline_render_info; result = vkCreateGraphicsPipelines(device, 0, 1, &cube_create_info, NULL, &renderer.vk.pipe.pipelines[PIPELINE_CUBE]); if (result != VK_SUCCESS) { Printf("vkCreateGraphicsPipelines failure: %s", VkResultStr(result)); success = false; } vkDestroyShaderModule(device, cube_vert, NULL); vkDestroyShaderModule(device, cube_frag, NULL); return success; } static b32 CreateShaderModule(u8 *bytes, u32 len, VkShaderModule *module) { VkResult result; b32 success = true; VkShaderModuleCreateInfo module_info = { .sType = STYPE(SHADER_MODULE_CREATE_INFO), .codeSize = len, .pCode = (u32 *)bytes, }; result = vkCreateShaderModule(renderer.vk.device, &module_info, NULL, module); if (result != VK_SUCCESS) { Printf("vkCreateShaderModule failure: %s", VkResultStr(result)); success = false; } return success; } static void DestroySwapchain() { for (u32 i = 0; i < renderer.vk.sc.img_count; i++) { vkDestroyImageView(renderer.vk.device, renderer.vk.sc.views[i], NULL); } vkDestroySwapchainKHR(renderer.vk.device, renderer.vk.swapchain, NULL); } static void DestroyDrawImages() { SwapchainStructures *sc = &renderer.vk.sc; VkDevice device = renderer.vk.device; VmaAllocator vma_alloc = renderer.vk.alloc; vkDestroyImageView(device, sc->draw_img.view, NULL); vmaDestroyImage(vma_alloc, sc->draw_img.img, sc->draw_img.alloc); vkDestroyImageView(device, sc->depth_img.view, NULL); vmaDestroyImage(vma_alloc, sc->depth_img.img, sc->depth_img.alloc); } static void DestroyVulkan() { VkDevice device = renderer.vk.device; VkInstance instance = renderer.vk.inst; FrameStructures data = renderer.vk.frame; ImmediateStructures imm = renderer.vk.imm; SwapchainStructures sc = renderer.vk.sc; VmaAllocator vma_alloc = renderer.vk.alloc; VkSwapchainKHR swapchain = renderer.vk.swapchain; PipelineStructures pipe = renderer.vk.pipe; vkDeviceWaitIdle(device); for (u32 i = PIPELINE_CUBE; i < PIPELINE_MAX; i++) vkDestroyPipeline(device, pipe.pipelines[i], NULL); vkDestroyPipelineLayout(device, pipe.pipeline_layout, NULL); for (u32 i = DESC_TYPE_SHARED; i < DESC_TYPE_MAX; i++) vkDestroyDescriptorSetLayout(device, pipe.layouts[i], NULL); vkDestroyDescriptorPool(device, pipe.pool, NULL); DestroyDrawImages(); DestroySwapchain(); vkDestroyFence(device, imm.fence, NULL); vkFreeCommandBuffers(device, imm.pool, 1, &imm.buffer); vkDestroyCommandPool(device, imm.pool, NULL); for (u32 i = 0; i < sc.img_count; i++) { vkDestroySemaphore(device, data.render_sems[i], NULL); vkDestroySemaphore(device, data.swapchain_sems[i], NULL); vkDestroyFence(device, data.render_fences[i], NULL); vkFreeCommandBuffers(device, data.pools[i], 1, &data.buffers[i]); vkDestroyCommandPool(device, data.pools[i], NULL); } vmaDestroyAllocator(renderer.vk.alloc); vkDestroyDevice(renderer.vk.device, NULL); vkDestroySurfaceKHR(renderer.vk.inst, renderer.vk.surface, NULL); #ifdef BUILD_DEBUG { vkDestroyDebugUtilsMessengerEXT(renderer.vk.inst, renderer.vk.debug, NULL); } #endif vkDestroyInstance(renderer.vk.inst, NULL); } const char *VkResultStr(VkResult result) { switch (result) { case VK_SUCCESS: return "VK_SUCCESS"; case VK_NOT_READY: return "VK_NOT_READY"; case VK_TIMEOUT: return "VK_TIMEOUT"; case VK_EVENT_SET: return "VK_EVENT_SET"; case VK_EVENT_RESET: return "VK_EVENT_RESET"; case VK_INCOMPLETE: return "VK_INCOMPLETE"; case VK_ERROR_OUT_OF_HOST_MEMORY: return "VK_ERROR_OUT_OF_HOST_MEMORY"; case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "VK_ERROR_OUT_OF_DEVICE_MEMORY"; case VK_ERROR_INITIALIZATION_FAILED: return "VK_ERROR_INITIALIZATION_FAILED"; case VK_ERROR_DEVICE_LOST: return "VK_ERROR_DEVICE_LOST"; case VK_ERROR_MEMORY_MAP_FAILED: return "VK_ERROR_MEMORY_MAP_FAILED"; case VK_ERROR_LAYER_NOT_PRESENT: return "VK_ERROR_LAYER_NOT_PRESENT"; case VK_ERROR_EXTENSION_NOT_PRESENT: return "VK_ERROR_EXTENSION_NOT_PRESENT"; case VK_ERROR_FEATURE_NOT_PRESENT: return "VK_ERROR_FEATURE_NOT_PRESENT"; case VK_ERROR_INCOMPATIBLE_DRIVER: return "VK_ERROR_INCOMPATIBLE_DRIVER"; case VK_ERROR_TOO_MANY_OBJECTS: return "VK_ERROR_TOO_MANY_OBJECTS"; case VK_ERROR_FORMAT_NOT_SUPPORTED: return "VK_ERROR_FORMAT_NOT_SUPPORTED"; case VK_ERROR_FRAGMENTED_POOL: return "VK_ERROR_FRAGMENTED_POOL"; case VK_ERROR_UNKNOWN: return "VK_ERROR_UNKNOWN"; case VK_ERROR_OUT_OF_POOL_MEMORY: return "VK_ERROR_OUT_OF_POOL_MEMORY"; case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "VK_ERROR_INVALID_EXTERNAL_HANDLE"; case VK_ERROR_FRAGMENTATION: return "VK_ERROR_FRAGMENTATION"; case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS"; case VK_PIPELINE_COMPILE_REQUIRED: return "VK_PIPELINE_COMPILE_REQUIRED"; case VK_ERROR_SURFACE_LOST_KHR: return "VK_ERROR_SURFACE_LOST_KHR"; case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR"; case VK_SUBOPTIMAL_KHR: return "VK_SUBOPTIMAL_KHR"; case VK_ERROR_OUT_OF_DATE_KHR: return "VK_ERROR_OUT_OF_DATE_KHR"; case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR"; case VK_ERROR_VALIDATION_FAILED_EXT: return "VK_ERROR_VALIDATION_FAILED_EXT"; case VK_ERROR_INVALID_SHADER_NV: return "VK_ERROR_INVALID_SHADER_NV"; #ifdef VK_ENABLE_BETA_EXTENSIONS case VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR: return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR"; #endif #ifdef VK_ENABLE_BETA_EXTENSIONS case VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR: return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR"; #endif #ifdef VK_ENABLE_BETA_EXTENSIONS case VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR: return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR"; #endif #ifdef VK_ENABLE_BETA_EXTENSIONS case VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR: return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR"; #endif #ifdef VK_ENABLE_BETA_EXTENSIONS case VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR: return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR"; #endif #ifdef VK_ENABLE_BETA_EXTENSIONS case VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR: return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR"; #endif case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; case VK_ERROR_NOT_PERMITTED_KHR: return "VK_ERROR_NOT_PERMITTED_KHR"; case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT"; case VK_THREAD_IDLE_KHR: return "VK_THREAD_IDLE_KHR"; case VK_THREAD_DONE_KHR: return "VK_THREAD_DONE_KHR"; case VK_OPERATION_DEFERRED_KHR: return "VK_OPERATION_DEFERRED_KHR"; case VK_OPERATION_NOT_DEFERRED_KHR: return "VK_OPERATION_NOT_DEFERRED_KHR"; case VK_ERROR_COMPRESSION_EXHAUSTED_EXT: return "VK_ERROR_COMPRESSION_EXHAUSTED_EXT"; case VK_RESULT_MAX_ENUM: return "VK_RESULT_MAX_ENUM"; default: return "??????"; } } static VkBool32 DebugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_type, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData ) { char *ms, *mt; switch (message_severity) { case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: ms = (char *)"VERBOSE"; break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: ms = (char *)"INFO"; break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: ms = (char *)"WARNING"; break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: ms = (char *)"ERROR"; break; default: ms = (char *)"UNKNOWN"; break; } switch (message_type) { case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: mt = (char *)"General"; break; case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: mt = (char *)"Validation"; break; case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: mt = (char *)"Validation | General"; break; case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: mt = (char *)"Performance"; break; case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: mt = (char *)"General | Performance"; break; case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: mt = (char *)"Validation | Performance"; break; case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: mt = (char *)"General | Validation | Performance"; break; default: mt = (char *)"Unknown"; break; } Printf("[%s: %s]\n%s\n", ms, mt, pCallbackData->pMessage); return VK_FALSE; }