module; #include #include #include #include #include #include #include #include #include #include #include "VulkanInitializers.hpp" #include "VulkanTransition.hpp" module Crafter.Graphics; import Crafter.Event; using namespace Crafter; void WindowWaylandVulkan::CreateSwapchain() { // Store the current swap chain handle so we can use it later on to ease up recreation VkSwapchainKHR oldSwapchain = swapChain; // Get physical device surface properties and formats VkSurfaceCapabilitiesKHR surfCaps; VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VulkanDevice::physDevice, vulkanSurface, &surfCaps)); VkExtent2D swapchainExtent = {}; // If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain if (surfCaps.currentExtent.width == (uint32_t)-1) { // If the surface size is undefined, the size is set to the size of the images requested swapchainExtent.width = width; swapchainExtent.height = height; } else { // If the surface size is defined, the swap chain size must match swapchainExtent = surfCaps.currentExtent; width = surfCaps.currentExtent.width; height = surfCaps.currentExtent.height; } // Select a present mode for the swapchain uint32_t presentModeCount; VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(VulkanDevice::physDevice, vulkanSurface, &presentModeCount, NULL)); assert(presentModeCount > 0); std::vector presentModes(presentModeCount); VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(VulkanDevice::physDevice, vulkanSurface, &presentModeCount, presentModes.data())); // The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec // This mode waits for the vertical blank ("v-sync") VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; // Determine the number of images uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1; if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount)) { desiredNumberOfSwapchainImages = surfCaps.maxImageCount; } // Find the transformation of the surface VkSurfaceTransformFlagsKHR preTransform; if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { // We prefer a non-rotated transform preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } else { preTransform = surfCaps.currentTransform; } // Find a supported composite alpha format (not all devices support alpha opaque) VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // Simply select the first composite alpha format available std::vector compositeAlphaFlags = { VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, }; for (auto& compositeAlphaFlag : compositeAlphaFlags) { if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) { compositeAlpha = compositeAlphaFlag; break; }; } VkSwapchainCreateInfoKHR swapchainCI = {}; swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchainCI.surface = vulkanSurface; swapchainCI.minImageCount = desiredNumberOfSwapchainImages; swapchainCI.imageFormat = colorFormat; swapchainCI.imageColorSpace = colorSpace; swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height }; swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform; swapchainCI.imageArrayLayers = 1; swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchainCI.queueFamilyIndexCount = 0; swapchainCI.presentMode = swapchainPresentMode; // Setting oldSwapChain to the saved handle of the previous swapchain aids in resource reuse and makes sure that we can still present already acquired images swapchainCI.oldSwapchain = oldSwapchain; // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area swapchainCI.clipped = VK_TRUE; swapchainCI.compositeAlpha = compositeAlpha; // Enable transfer source on swap chain images if supported if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) { swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } // Enable transfer destination on swap chain images if supported if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) { swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } VulkanDevice::CHECK_VK_RESULT(vkCreateSwapchainKHR(VulkanDevice::device, &swapchainCI, nullptr, &swapChain)); // If an existing swap chain is re-created, destroy the old swap chain and the ressources owned by the application (image views, images are owned by the swap chain) if (oldSwapchain != VK_NULL_HANDLE) { for (auto i = 0; i < images.size(); i++) { vkDestroyImageView(VulkanDevice::device, imageViews[i], nullptr); } vkDestroySwapchainKHR(VulkanDevice::device, oldSwapchain, nullptr); } uint32_t imageCount{ 0 }; VulkanDevice::CHECK_VK_RESULT(vkGetSwapchainImagesKHR(VulkanDevice::device, swapChain, &imageCount, nullptr)); // Get the swap chain images images.resize(imageCount); VulkanDevice::CHECK_VK_RESULT(vkGetSwapchainImagesKHR(VulkanDevice::device, swapChain, &imageCount, images.data())); // Get the swap chain buffers containing the image and imageview imageViews.resize(imageCount); for (auto i = 0; i < images.size(); i++) { VkImageViewCreateInfo colorAttachmentView = {}; colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; colorAttachmentView.pNext = NULL; colorAttachmentView.format = colorFormat; colorAttachmentView.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; colorAttachmentView.subresourceRange.baseMipLevel = 0; colorAttachmentView.subresourceRange.levelCount = 1; colorAttachmentView.subresourceRange.baseArrayLayer = 0; colorAttachmentView.subresourceRange.layerCount = 1; colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; colorAttachmentView.flags = 0; colorAttachmentView.image = images[i]; VulkanDevice::CHECK_VK_RESULT(vkCreateImageView(VulkanDevice::device, &colorAttachmentView, nullptr, &imageViews[i])); } } WindowWaylandVulkan::WindowWaylandVulkan(std::string name, std::uint32_t width, std::uint32_t height) : WindowWayland(name, width, height) { VkWaylandSurfaceCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; createInfo.display = display; createInfo.surface = surface; VulkanDevice::CHECK_VK_RESULT(vkCreateWaylandSurfaceKHR(VulkanDevice::instance, &createInfo, NULL, &vulkanSurface)); // Get list of supported surface formats std::uint32_t formatCount; VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanDevice::physDevice, vulkanSurface, &formatCount, NULL)); assert(formatCount > 0); std::vector surfaceFormats(formatCount); VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanDevice::physDevice, vulkanSurface, &formatCount, surfaceFormats.data())); // We want to get a format that best suits our needs, so we try to get one from a set of preferred formats // Initialize the format to the first one returned by the implementation in case we can't find one of the preffered formats VkSurfaceFormatKHR selectedFormat = surfaceFormats[0]; std::vector preferredImageFormats = { VK_FORMAT_R8G8B8A8_UNORM, }; for (auto& availableFormat : surfaceFormats) { if (std::find(preferredImageFormats.begin(), preferredImageFormats.end(), availableFormat.format) != preferredImageFormats.end()) { selectedFormat = availableFormat; break; } } colorFormat = selectedFormat.format; colorSpace = selectedFormat.colorSpace; CreateSwapchain(); std::array attachments = {}; // Color attachment attachments[0].format = colorFormat; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // Depth attachment attachments[1].format = VulkanDevice::depthFormat; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference colorReference = {}; colorReference.attachment = 0; colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference depthReference = {}; depthReference.attachment = 1; depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkSubpassDescription subpassDescription = {}; subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpassDescription.colorAttachmentCount = 1; subpassDescription.pColorAttachments = &colorReference; subpassDescription.pDepthStencilAttachment = &depthReference; subpassDescription.inputAttachmentCount = 0; subpassDescription.pInputAttachments = nullptr; subpassDescription.preserveAttachmentCount = 0; subpassDescription.pPreserveAttachments = nullptr; subpassDescription.pResolveAttachments = nullptr; // Subpass dependencies for layout transitions std::array dependencies{}; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0; dependencies[0].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; dependencies[0].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; dependencies[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dependencies[0].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; dependencies[0].dependencyFlags = 0; dependencies[1].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].dstSubpass = 0; dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].srcAccessMask = 0; dependencies[1].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; dependencies[1].dependencyFlags = 0; VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.attachmentCount = static_cast(attachments.size()); renderPassInfo.pAttachments = attachments.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpassDescription; renderPassInfo.dependencyCount = static_cast(dependencies.size()); renderPassInfo.pDependencies = dependencies.data(); VulkanDevice::CHECK_VK_RESULT(vkCreateRenderPass(VulkanDevice::device, &renderPassInfo, nullptr, &renderPass)); VkImageCreateInfo imageCI{}; imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.format = VulkanDevice::depthFormat; imageCI.extent = { width, height, 1 }; imageCI.mipLevels = 1; imageCI.arrayLayers = 1; imageCI.samples = VK_SAMPLE_COUNT_1_BIT; imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; imageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; VulkanDevice::CHECK_VK_RESULT(vkCreateImage(VulkanDevice::device, &imageCI, nullptr, &depthStencil.image)); VkMemoryRequirements memReqs{}; vkGetImageMemoryRequirements(VulkanDevice::device, depthStencil.image, &memReqs); VkMemoryAllocateInfo memAllloc{}; memAllloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllloc.allocationSize = memReqs.size; memAllloc.memoryTypeIndex = VulkanDevice::GetMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VulkanDevice::CHECK_VK_RESULT(vkAllocateMemory(VulkanDevice::device, &memAllloc, nullptr, &depthStencil.memory)); VulkanDevice::CHECK_VK_RESULT(vkBindImageMemory(VulkanDevice::device, depthStencil.image, depthStencil.memory, 0)); VkImageViewCreateInfo imageViewCI{}; imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewCI.image = depthStencil.image; imageViewCI.format = VulkanDevice::depthFormat; imageViewCI.subresourceRange.baseMipLevel = 0; imageViewCI.subresourceRange.levelCount = 1; imageViewCI.subresourceRange.baseArrayLayer = 0; imageViewCI.subresourceRange.layerCount = 1; imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; VulkanDevice::CHECK_VK_RESULT(vkCreateImageView(VulkanDevice::device, &imageViewCI, nullptr, &depthStencil.view)); // Create frame buffers for every swap chain image frameBuffers.resize(images.size()); for (uint32_t i = 0; i < frameBuffers.size(); i++) { const VkImageView attachments[2] = { imageViews[i], depthStencil.view }; VkFramebufferCreateInfo frameBufferCreateInfo{}; frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; frameBufferCreateInfo.renderPass = renderPass; frameBufferCreateInfo.attachmentCount = 2; frameBufferCreateInfo.pAttachments = attachments; frameBufferCreateInfo.width = width; frameBufferCreateInfo.height = height; frameBufferCreateInfo.layers = 1; VulkanDevice::CHECK_VK_RESULT(vkCreateFramebuffer(VulkanDevice::device, &frameBufferCreateInfo, nullptr, &frameBuffers[i])); } drawCmdBuffers.resize(images.size()); VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(VulkanDevice::commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, static_cast(drawCmdBuffers.size())); VulkanDevice::CHECK_VK_RESULT(vkAllocateCommandBuffers(VulkanDevice::device, &cmdBufAllocateInfo, drawCmdBuffers.data())); VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo(); VulkanDevice::CHECK_VK_RESULT(vkCreateSemaphore(VulkanDevice::device, &semaphoreCreateInfo, nullptr, &semaphores.presentComplete)); VulkanDevice::CHECK_VK_RESULT(vkCreateSemaphore(VulkanDevice::device, &semaphoreCreateInfo, nullptr, &semaphores.renderComplete)); // Set up submit info structure // Semaphores will stay the same during application lifetime // Command buffer submission info is set by each example submitInfo = vks::initializers::submitInfo(); submitInfo.pWaitDstStageMask = &submitPipelineStages; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = &semaphores.presentComplete; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &semaphores.renderComplete; } WindowWaylandVulkan::~WindowWaylandVulkan() { if (swapChain != VK_NULL_HANDLE) { for (auto i = 0; i < images.size(); i++) { vkDestroyImageView(VulkanDevice::device, imageViews[i], nullptr); } vkDestroySwapchainKHR(VulkanDevice::device, swapChain, nullptr); } if (vulkanSurface != VK_NULL_HANDLE) { vkDestroySurfaceKHR(VulkanDevice::instance, vulkanSurface, nullptr); } } void WindowWaylandVulkan::Start() { VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); VkClearValue clearValues[2]; clearValues[0].color = { };; clearValues[1].depthStencil = { 1.0f, 0 }; for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { VulkanDevice::CHECK_VK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); VkImageSubresourceRange range{}; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; range.baseMipLevel = 0; range.levelCount = VK_REMAINING_MIP_LEVELS; range.baseArrayLayer = 0; range.layerCount = VK_REMAINING_ARRAY_LAYERS; VkImageSubresourceRange depth_range{range}; depth_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; image_layout_transition(drawCmdBuffers[i], images[i], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, range); image_layout_transition(drawCmdBuffers[i], depthStencil.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, depth_range); VkRenderingAttachmentInfoKHR color_attachment_info = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, VK_NULL_HANDLE}; color_attachment_info.imageView = imageViews[i]; color_attachment_info.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; color_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE; color_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_STORE; color_attachment_info.clearValue = { 0.0f, 0.0f, 0.2f, 1.0f }; VkRenderingAttachmentInfoKHR depth_attachment_info = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, VK_NULL_HANDLE}; depth_attachment_info.imageView = depthStencil.view; depth_attachment_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; depth_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE; depth_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; depth_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; depth_attachment_info.clearValue = { 1.0f, 0 }; VkRenderingInfo render_info = {VK_STRUCTURE_TYPE_RENDERING_INFO_KHR,VK_NULL_HANDLE,0}; render_info.renderArea = VkRect2D{VkOffset2D{}, VkExtent2D{width, height}}; render_info.viewMask = 0; render_info.layerCount = 1; render_info.colorAttachmentCount = 1; render_info.pColorAttachments = &color_attachment_info; render_info.pDepthAttachment = &depth_attachment_info; render_info.pStencilAttachment = VK_NULL_HANDLE; VulkanDevice::vkCmdBeginRenderingKHRProc(drawCmdBuffers[i], &render_info); VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); for(VulkanElement* element : vulkanElements.components) { vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, element->pipelineLayout, 0, 2, element->descriptorSet, 0, NULL); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, element->pipeline); VulkanDevice::vkCmdDrawMeshTasksEXTProc(drawCmdBuffers[i], element->sizeX, element->sizeY, element->sizeZ); VulkanDevice::vkCmdEndRenderingKHRProc(drawCmdBuffers[i]); } image_layout_transition(drawCmdBuffers[i], images[i], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, range ); VulkanDevice::CHECK_VK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); } while (open && wl_display_dispatch(display) != -1) { // Acquire the next image from the swap chain VulkanDevice::CHECK_VK_RESULT(vkAcquireNextImageKHR(VulkanDevice::device, swapChain, UINT64_MAX, semaphores.presentComplete, (VkFence)nullptr, ¤tBuffer)); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; VulkanDevice::CHECK_VK_RESULT(vkQueueSubmit(VulkanDevice::queue, 1, &submitInfo, VK_NULL_HANDLE)); VkPresentInfoKHR presentInfo = {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.pNext = NULL; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = &swapChain; presentInfo.pImageIndices = ¤tBuffer; // Check if a wait semaphore has been specified to wait for before presenting the image if (semaphores.renderComplete != VK_NULL_HANDLE) { presentInfo.pWaitSemaphores = &semaphores.renderComplete; presentInfo.waitSemaphoreCount = 1; } VulkanDevice::CHECK_VK_RESULT(vkQueuePresentKHR(VulkanDevice::queue, &presentInfo)); VulkanDevice::CHECK_VK_RESULT(vkQueueWaitIdle(VulkanDevice::queue)); } }