// Requires gcc, the Vulkan SDK, and libglfw3-dev // On Linux, compile with: // gcc -Wall -o fail fail.c -Ibuild/_deps/glfw-src/include/ -lglfw -lvulkan #include #include #include #include #include #include #include const uint32_t WIDTH = 800; const uint32_t HEIGHT = 600; #define STR(r) \ case VK_##r: \ str = #r; \ break #define noop static inline int check_result(VkResult res) { char* str = "UNKNOWN_ERROR"; switch (res) { STR(NOT_READY); STR(TIMEOUT); STR(EVENT_SET); STR(EVENT_RESET); STR(INCOMPLETE); STR(ERROR_OUT_OF_HOST_MEMORY); STR(ERROR_OUT_OF_DEVICE_MEMORY); STR(ERROR_INITIALIZATION_FAILED); STR(ERROR_DEVICE_LOST); STR(ERROR_MEMORY_MAP_FAILED); STR(ERROR_LAYER_NOT_PRESENT); STR(ERROR_EXTENSION_NOT_PRESENT); STR(ERROR_FEATURE_NOT_PRESENT); STR(ERROR_INCOMPATIBLE_DRIVER); STR(ERROR_TOO_MANY_OBJECTS); STR(ERROR_FORMAT_NOT_SUPPORTED); STR(ERROR_SURFACE_LOST_KHR); STR(ERROR_NATIVE_WINDOW_IN_USE_KHR); STR(SUBOPTIMAL_KHR); STR(ERROR_OUT_OF_DATE_KHR); STR(ERROR_INCOMPATIBLE_DISPLAY_KHR); STR(ERROR_VALIDATION_FAILED_EXT); STR(ERROR_INVALID_SHADER_NV); default: noop; } if (res != VK_SUCCESS) { printf("VkResult is %s in %s at line %d\n", str, __FILE__, __LINE__); return 1; } return 0; } #define VK_CHECK_RESULT(f) \ { \ VkResult res = (f); \ check_result(res); \ } static VkResult create_debug_utils_messenger_EXT( VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( instance, "vkCreateDebugUtilsMessengerEXT"); if (func != NULL) { return func(instance, pCreateInfo, pAllocator, pDebugMessenger); } else { return VK_ERROR_EXTENSION_NOT_PRESENT; } } static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { // Hide long list of extensions. if (strstr(pCallbackData->pMessage, "Extension: VK_") != NULL) return VK_FALSE; printf("validation layer: %s\n", pCallbackData->pMessage); if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT && pUserData != NULL) { uint32_t* n_errors = (uint32_t*)pUserData; (*n_errors)++; } return VK_FALSE; } static void destroy_debug_utils_messenger_EXT( VkInstance instance, VkDebugUtilsMessengerEXT debug_messenger, const VkAllocationCallbacks* pAllocator) { PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( instance, "vkDestroyDebugUtilsMessengerEXT"); if (func != NULL) { func(instance, debug_messenger, pAllocator); } } static VkInstance createInstance(VkDebugUtilsMessengerEXT* debug_messenger, void* debug_data) { uint32_t glfwExtensionCount = 0; const char** glfwExtensions; glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); const char* addExtensions[] = { "VK_EXT_debug_utils", // }; uint32_t addExtensionCount = sizeof(addExtensions) / sizeof(char*); uint32_t extensionCount = glfwExtensionCount + addExtensionCount; char** extensions = calloc(extensionCount, sizeof(char*)); memcpy(extensions, glfwExtensions, glfwExtensionCount * sizeof(char*)); memcpy(&extensions[glfwExtensionCount], addExtensions, sizeof(addExtensions)); // Prepare the creation of the Vulkan instance. VkApplicationInfo appInfo = {0}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "fail"; appInfo.applicationVersion = 1; appInfo.pEngineName = "fail"; appInfo.engineVersion = 1; appInfo.apiVersion = VK_API_VERSION_1_3; VkInstanceCreateInfo info_inst = {0}; info_inst.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; info_inst.pApplicationInfo = &appInfo; info_inst.enabledExtensionCount = extensionCount; info_inst.ppEnabledExtensionNames = (const char* const*)extensions; // Validation layers. VkDebugUtilsMessengerCreateInfoEXT info_debug = {0}; info_debug.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; info_debug.flags = 0; info_debug.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; info_debug.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; info_debug.pfnUserCallback = debug_callback; info_debug.pUserData = debug_data; VkValidationFeatureEnableEXT enables[16] = {0}; VkValidationFeaturesEXT features = {0}; info_inst.enabledLayerCount = 1; info_inst.ppEnabledLayerNames = (const char*[]){"VK_LAYER_KHRONOS_validation"}; // https://vulkan.lunarg.com/doc/sdk/1.2.170.0/linux/best_practices.html enables[0] = VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT; enables[1] = VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; enables[2] = VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT; features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; features.enabledValidationFeatureCount = 3; features.pEnabledValidationFeatures = enables; // pNext chain features.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&info_debug; info_inst.pNext = &features; // Create the Vulkan instance. VkInstance instance; VK_CHECK_RESULT(vkCreateInstance(&info_inst, NULL, &instance)); // Create the debug utils messenger. VK_CHECK_RESULT( create_debug_utils_messenger_EXT(instance, &info_debug, NULL, debug_messenger)); free(extensions); return instance; } VkPhysicalDevice pickPhysicalDevice(VkInstance instance) { VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, NULL); if (deviceCount == 0) { fprintf(stderr, "Failed to find GPUs with Vulkan support!\n"); exit(1); } VkPhysicalDevice devices[deviceCount]; vkEnumeratePhysicalDevices(instance, &deviceCount, devices); // Just pick the first device for simplicity physicalDevice = devices[0]; return physicalDevice; } VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, uint32_t* queueFamilyIndex) { VkDevice device; VkDeviceQueueCreateInfo queueCreateInfo = {0}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = *queueFamilyIndex; queueCreateInfo.queueCount = 1; float queuePriority = 1.0f; queueCreateInfo.pQueuePriorities = &queuePriority; VkPhysicalDeviceFeatures deviceFeatures = {0}; vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); VkDeviceCreateInfo createInfo = {0}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.pQueueCreateInfos = &queueCreateInfo; createInfo.queueCreateInfoCount = 1; createInfo.pEnabledFeatures = &deviceFeatures; createInfo.enabledExtensionCount = 1; createInfo.ppEnabledExtensionNames = (const char*[]){ VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; if (vkCreateDevice(physicalDevice, &createInfo, NULL, &device) != VK_SUCCESS) { fprintf(stderr, "Failed to create logical device!\n"); exit(1); } return device; } uint32_t findQueueFamilies(VkPhysicalDevice device) { uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, NULL); VkQueueFamilyProperties queueFamilies[queueFamilyCount]; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies); for (uint32_t i = 0; i < queueFamilyCount; i++) { if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { return i; } } fprintf(stderr, "Failed to find a suitable queue family!\n"); exit(1); } VkSurfaceKHR createSurface(VkInstance instance, GLFWwindow* window) { VkSurfaceKHR surface; if (glfwCreateWindowSurface(instance, window, NULL, &surface) != VK_SUCCESS) { fprintf(stderr, "Failed to create window surface!\n"); exit(1); } return surface; } VkSwapchainKHR createSwapChain( VkPhysicalDevice physicalDevice, VkDevice device, VkSurfaceKHR surface, VkFormat* format, uint32_t* imageCount, VkExtent2D* extent, VkImage* swapChainImages) { assert(device); VkSwapchainKHR swapChain; VkSurfaceCapabilitiesKHR capabilities; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &capabilities); VkSurfaceFormatKHR surfaceFormat; uint32_t formatCount; vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL); VkSurfaceFormatKHR formats[formatCount]; vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, formats); surfaceFormat = formats[0]; VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; VkSwapchainCreateInfoKHR createInfo = {0}; createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; createInfo.surface = surface; createInfo.minImageCount = capabilities.minImageCount + 1; createInfo.imageFormat = surfaceFormat.format; createInfo.imageColorSpace = surfaceFormat.colorSpace; createInfo.imageExtent = capabilities.currentExtent; createInfo.imageArrayLayers = 1; createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; createInfo.preTransform = capabilities.currentTransform; createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; createInfo.oldSwapchain = VK_NULL_HANDLE; if (vkCreateSwapchainKHR(device, &createInfo, NULL, &swapChain) != VK_SUCCESS) { fprintf(stderr, "Failed to create swap chain!\n"); exit(1); } *extent = createInfo.imageExtent; vkGetSwapchainImagesKHR(device, swapChain, imageCount, NULL); vkGetSwapchainImagesKHR(device, swapChain, imageCount, swapChainImages); *format = createInfo.imageFormat; return swapChain; } VkImageView createImageView(VkDevice device, VkImage image, VkFormat format) { VkImageViewCreateInfo viewInfo = {0}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = format; viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; VkImageView imageView; if (vkCreateImageView(device, &viewInfo, NULL, &imageView) != VK_SUCCESS) { fprintf(stderr, "Failed to create image views!\n"); exit(1); } return imageView; } VkRenderPass createRenderPass(VkDevice device, VkFormat format) { VkRenderPass renderPass; VkAttachmentDescription colorAttachment = {0}; colorAttachment.format = format; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; VkAttachmentReference colorAttachmentRef = {0}; colorAttachmentRef.attachment = 0; colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass = {0}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; VkRenderPassCreateInfo renderPassInfo = {0}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.attachmentCount = 1; renderPassInfo.pAttachments = &colorAttachment; renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; if (vkCreateRenderPass(device, &renderPassInfo, NULL, &renderPass) != VK_SUCCESS) { fprintf(stderr, "Failed to create render pass!\n"); exit(1); } return renderPass; } VkFramebuffer createFramebuffer( VkDevice device, VkRenderPass renderPass, VkImageView imageView, VkExtent2D extent) { VkFramebuffer framebuffer; VkImageView attachments[] = {imageView}; VkFramebufferCreateInfo framebufferInfo = {0}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = renderPass; framebufferInfo.attachmentCount = 1; framebufferInfo.pAttachments = attachments; framebufferInfo.width = extent.width; framebufferInfo.height = extent.height; framebufferInfo.layers = 1; if (vkCreateFramebuffer(device, &framebufferInfo, NULL, &framebuffer) != VK_SUCCESS) { fprintf(stderr, "Failed to create framebuffer!\n"); exit(1); } return framebuffer; } VkCommandPool createCommandPool(VkDevice device, uint32_t queueFamilyIndex) { VkCommandPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndex; poolInfo.flags = 0; VkCommandPool commandPool; if (vkCreateCommandPool(device, &poolInfo, NULL, &commandPool) != VK_SUCCESS) { fprintf(stderr, "Failed to create command pool\n"); exit(1); } return commandPool; } VkCommandBuffer* createCommandBuffers( VkDevice device, VkCommandPool commandPool, VkRenderPass renderPass, VkImage image, VkFramebuffer* framebuffers, VkExtent2D extent, uint32_t imageCount) { VkCommandBuffer* commandBuffers = malloc(imageCount * sizeof(VkCommandBuffer)); VkCommandBufferAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.commandPool = commandPool; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandBufferCount = imageCount; vkAllocateCommandBuffers(device, &allocInfo, commandBuffers); VkImageMemoryBarrier imageBarrier = {}; imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imageBarrier.image = image; imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBarrier.subresourceRange.baseMipLevel = 0; imageBarrier.subresourceRange.levelCount = 1; imageBarrier.subresourceRange.baseArrayLayer = 0; imageBarrier.subresourceRange.layerCount = 1; for (size_t i = 0; i < imageCount; i++) { VkCommandBufferBeginInfo beginInfo = {}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkBeginCommandBuffer(commandBuffers[i], &beginInfo); VkRenderPassBeginInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = renderPass; renderPassInfo.framebuffer = framebuffers[i]; renderPassInfo.renderArea.offset = (VkOffset2D){0, 0}; renderPassInfo.renderArea.extent = extent; VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; renderPassInfo.clearValueCount = 1; renderPassInfo.pClearValues = &clearColor; vkCmdPipelineBarrier( commandBuffers[i], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &imageBarrier); vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdEndRenderPass(commandBuffers[i]); vkEndCommandBuffer(commandBuffers[i]); } return commandBuffers; } VkSemaphore createSemaphore(VkDevice device) { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; VkSemaphore semaphore; if (vkCreateSemaphore(device, &semaphoreInfo, NULL, &semaphore) != VK_SUCCESS) { fprintf(stderr, "Failed to create semaphore\n"); exit(1); } return semaphore; } int main() { if (!glfwInit()) { fprintf(stderr, "Failed to initialize GLFW\n"); return -1; } glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan Window", NULL, NULL); if (!window) { fprintf(stderr, "Failed to create GLFW window\n"); glfwTerminate(); return -1; } VkDebugUtilsMessengerEXT debug_callback; VkInstance instance = createInstance(&debug_callback, NULL); VkPhysicalDevice physicalDevice = pickPhysicalDevice(instance); uint32_t queueFamilyIndex = findQueueFamilies(physicalDevice); VkDevice device = createLogicalDevice(physicalDevice, &queueFamilyIndex); VkSurfaceKHR surface = createSurface(instance, window); VkFormat format; uint32_t imageCount; VkExtent2D extent; VkImage swapChainImages[10] = {0}; VkSwapchainKHR swapChain = createSwapChain( physicalDevice, device, surface, &format, &imageCount, &extent, swapChainImages); VkImageView* imageViews = malloc(imageCount * sizeof(VkImageView)); for (uint32_t i = 0; i < imageCount; i++) imageViews[i] = createImageView(device, swapChainImages[i], format); VkRenderPass renderPass = createRenderPass(device, format); VkFramebuffer* framebuffers = malloc(imageCount * sizeof(VkFramebuffer)); for (uint32_t i = 0; i < imageCount; i++) framebuffers[i] = createFramebuffer(device, renderPass, imageViews[i], extent); VkCommandPool commandPool = createCommandPool(device, queueFamilyIndex); VkCommandBuffer* commandBuffers = createCommandBuffers( device, commandPool, renderPass, swapChainImages[0], framebuffers, extent, imageCount); VkSemaphore imageAvailableSemaphore = createSemaphore(device); VkSemaphore renderFinishedSemaphore = createSemaphore(device); uint32_t imageIndex; vkAcquireNextImageKHR( device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; VkSemaphore waitSemaphores[] = {imageAvailableSemaphore}; // NOTE: the warning disappears by putting VK_PIPELINE_STAGE_ALL_COMMANDS_BIT here, // but then we've got: // // Validation Warning: [ UNASSIGNED-BestPractices-pipeline-stage-flags ] | MessageID = // 0x48a09f6c | vkQueueSubmit(): pSubmits[0].pWaitDstStageMask[0] using // VK_PIPELINE_STAGE_ALL_COMMANDS_BIT VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffers[imageIndex]; VkSemaphore signalSemaphores[] = {renderFinishedSemaphore}; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; VkQueue graphicsQueue; vkGetDeviceQueue(device, queueFamilyIndex, 0, &graphicsQueue); vkQueueSubmit(graphicsQueue, 1, &submitInfo, NULL); /* Here we get this: validation layer: Validation Error: [ SYNC-HAZARD-WRITE-AFTER-READ ] Object 0: handle = 0x5599afbbf520, type = VK_OBJECT_TYPE_QUEUE; | MessageID = 0x376bc9df | vkQueueSubmit(): Hazard WRITE_AFTER_READ for entry 0, VkCommandBuffer 0x5599b202e960[], Submitted access info (submitted_usage: SYNC_IMAGE_LAYOUT_TRANSITION, command: vkCmdBeginRenderPass, seq_no: 1, renderpass: VkRenderPass 0xcad092000000000d[], reset_no: 1). Access info (prior_usage: SYNC_PRESENT_ENGINE_SYNCVAL_PRESENT_ACQUIRE_READ_SYNCVAL, read_barriers: VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT|VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT, , batch_tag: 1, vkAcquireNextImageKHR aquire_tag:1: VkSwapchainKHR 0xf443490000000006[], image_index: 0image: VkImage 0xcb3ee80000000007[]). */ VkPresentInfoKHR presentInfo = {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = signalSemaphores; VkSwapchainKHR swapChains[] = {swapChain}; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapChains; presentInfo.pImageIndices = &imageIndex; vkQueuePresentKHR(graphicsQueue, &presentInfo); vkQueueWaitIdle(graphicsQueue); destroy_debug_utils_messenger_EXT(instance, debug_callback, NULL); vkDestroySemaphore(device, imageAvailableSemaphore, NULL); vkDestroySemaphore(device, renderFinishedSemaphore, NULL); for (uint32_t i = 0; i < imageCount; i++) { vkDestroyFramebuffer(device, framebuffers[i], NULL); vkDestroyImageView(device, imageViews[i], NULL); } vkDestroyCommandPool(device, commandPool, NULL); vkDestroyRenderPass(device, renderPass, NULL); vkDestroySwapchainKHR(device, swapChain, NULL); vkDestroyDevice(device, NULL); vkDestroySurfaceKHR(instance, surface, NULL); vkDestroyInstance(instance, NULL); glfwDestroyWindow(window); glfwTerminate(); return 0; }