static void cleanup_vulkan() { vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { vkDestroyFence(g_Device, g_Fence[i], g_Allocator); vkFreeCommandBuffers(g_Device, g_CommandPool[i], 1, &g_CommandBuffer[i]); vkDestroyCommandPool(g_Device, g_CommandPool[i], g_Allocator); vkDestroySemaphore(g_Device, g_PresentCompleteSemaphore[i], g_Allocator); vkDestroySemaphore(g_Device, g_RenderCompleteSemaphore[i], g_Allocator); } for (uint32_t i = 0; i < g_BackBufferCount; i++) { vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); } vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); vkDestroySwapchainKHR(g_Device, g_Swapchain, g_Allocator); vkDestroySurfaceKHR(g_Instance, g_Surface, g_Allocator); #ifdef IMGUI_VULKAN_DEBUG_REPORT // get the proc address of the function pointer, required for used extensions auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); vkDestroyDebugReportCallbackEXT(g_Instance, g_Debug_Report, g_Allocator); #endif // IMGUI_VULKAN_DEBUG_REPORT vkDestroyDevice(g_Device, g_Allocator); vkDestroyInstance(g_Instance, g_Allocator); }
VulkanCommon::~VulkanCommon() { if( Vulkan.Device != VK_NULL_HANDLE ) { vkDeviceWaitIdle( Vulkan.Device ); for( size_t i = 0; i < Vulkan.SwapChain.Images.size(); ++i ) { if( Vulkan.SwapChain.Images[i].ImageView != VK_NULL_HANDLE ) { vkDestroyImageView( GetDevice(), Vulkan.SwapChain.Images[i].ImageView, nullptr ); } } if( Vulkan.SwapChain.Handle != VK_NULL_HANDLE ) { vkDestroySwapchainKHR( Vulkan.Device, Vulkan.SwapChain.Handle, nullptr ); } vkDestroyDevice( Vulkan.Device, nullptr ); } if( Vulkan.PresentationSurface != VK_NULL_HANDLE ) { vkDestroySurfaceKHR( Vulkan.Instance, Vulkan.PresentationSurface, nullptr ); } if( Vulkan.Instance != VK_NULL_HANDLE ) { vkDestroyInstance( Vulkan.Instance, nullptr ); } if( VulkanLibrary ) { #if defined(VK_USE_PLATFORM_WIN32_KHR) FreeLibrary( VulkanLibrary ); #elif defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) dlclose( VulkanLibrary ); #endif } }
void VulkanSwapChain::clear(VkSwapchainKHR swapChain) { VkDevice logicalDevice = mDevice->getLogical(); if (swapChain != VK_NULL_HANDLE) { for (auto& surface : mSurfaces) { surface.framebuffer->destroy(); surface.framebuffer = nullptr; surface.image->destroy(); surface.image = nullptr; surface.sync->destroy(); surface.sync = nullptr; } vkDestroySwapchainKHR(logicalDevice, swapChain, gVulkanAllocator); } if (mDepthStencilImage != nullptr) { mDepthStencilImage->destroy(); mDepthStencilImage = nullptr; } }
void resize_vulkan_wm_swapchain(ReaperRoot& root, const VulkanBackend& backend, PresentationInfo& presentInfo, VkExtent2D extent) { REAPER_PROFILE_SCOPE("Vulkan", MP_RED); log_debug(root, "vulkan: resizing wm swapchain"); // Destroy what needs to be Assert(vkDeviceWaitIdle(backend.device) == VK_SUCCESS); vkDestroySemaphore(backend.device, presentInfo.imageAvailableSemaphore, nullptr); vkDestroySemaphore(backend.device, presentInfo.renderingFinishedSemaphore, nullptr); destroy_swapchain_framebuffers(backend, presentInfo); destroy_swapchain_renderpass(backend, presentInfo); vkDestroySwapchainKHR(backend.device, presentInfo.swapchain, nullptr); presentInfo.swapchain = VK_NULL_HANDLE; // Reconfigure even if we know most of what we expect/need SwapchainDescriptor swapchainDesc; swapchainDesc.preferredImageCount = presentInfo.imageCount; swapchainDesc.preferredFormat = presentInfo.surfaceFormat; swapchainDesc.preferredExtent = {extent.width, extent.height}; // New extent configure_vulkan_wm_swapchain(root, backend, swapchainDesc, presentInfo); create_vulkan_wm_swapchain(root, backend, presentInfo); }
void SwapChain::DestroySwapChain() { if (m_swap_chain == VK_NULL_HANDLE) return; vkDestroySwapchainKHR(g_vulkan_context->GetDevice(), m_swap_chain, nullptr); m_swap_chain = VK_NULL_HANDLE; }
void SurfaceWindow::resize(int w, int h) { mWidth = w; mHeight = h; vkDestroySwapchainKHR(mDevice, mSwapchain, nullptr); createSwapchain(); mNeedToInit = true; }
void VulkanWindow::FinalizeSwapchain() { if ( mVkSwapchainKHR != VK_NULL_HANDLE ) { vkDestroySwapchainKHR ( mVulkanRenderer.GetDevice(), mVkSwapchainKHR, nullptr ); mVkSwapchainKHR = VK_NULL_HANDLE; } }
void VulkanContext::DestroyObjects() { ILOG("VulkanContext::DestroyObjects (including swapchain)"); if (swapchain_ != VK_NULL_HANDLE) vkDestroySwapchainKHR(device_, swapchain_, nullptr); swapchain_ = VK_NULL_HANDLE; vkDestroySurfaceKHR(instance_, surface_, nullptr); surface_ = VK_NULL_HANDLE; }
void VkContext::Destroy() { vkDeviceWaitIdle(dev); // Free command buffers if (drawCmdBuffers.size() > 0) { vkFreeCommandBuffers(dev, cmdPool, (u32)drawCmdBuffers.size(), &drawCmdBuffers[0]); } // Destroy command pools vkDestroyCommandPool(dev, cmdPool, nullptr); // Destroy semaphores vkDestroySemaphore(dev, acquireCompleteSemaphore, nullptr); vkDestroySemaphore(dev, renderCompleteSemaphore, nullptr); // Destroy swapchain image views for (u32 i = 0; i < swapchainImageViews.size(); i++) { vkDestroyImageView(dev, swapchainImageViews[i], nullptr); } // Destroy swapchain if (swapchain) { vkDestroySwapchainKHR(dev, swapchain, nullptr); } // Destroy surface if (surf) { vkDestroySurfaceKHR(instance, surf, nullptr); } // Destroy device if (dev) { vkDestroyDevice(dev, nullptr); } #ifdef VOXL_DEBUG // Destroy debug report callback if (msgCallback != VK_NULL_HANDLE) { vkDestroyDebugReportCallbackEXT(instance, msgCallback, nullptr); } #endif // Destroy instance if (instance) { vkDestroyInstance(instance, nullptr); } // Terminate GLFW glfwTerminate(); }
void destroy_vulkan_wm_swapchain(ReaperRoot& root, const VulkanBackend& backend, PresentationInfo& presentInfo) { REAPER_PROFILE_SCOPE("Vulkan", MP_RED); log_debug(root, "vulkan: destroying wm swapchain"); vkDestroySemaphore(backend.device, presentInfo.imageAvailableSemaphore, nullptr); vkDestroySemaphore(backend.device, presentInfo.renderingFinishedSemaphore, nullptr); destroy_swapchain_framebuffers(backend, presentInfo); destroy_swapchain_renderpass(backend, presentInfo); vkDestroySwapchainKHR(backend.device, presentInfo.swapchain, nullptr); presentInfo.swapchain = VK_NULL_HANDLE; }
MicroSwapchain::~MicroSwapchain() { const VkDevice dev = m_factory->m_gr->getDevice(); for(TexturePtr& tex : m_textures) { tex.reset(nullptr); } if(m_swapchain) { vkDestroySwapchainKHR(dev, m_swapchain, nullptr); m_swapchain = {}; } }
SwapChain::~SwapChain() { if (VK_NULL_HANDLE != mVkSurfaceKHR) { const VulkanRenderer& vulkanRenderer = static_cast<VulkanRenderer&>(getRenderer()); const VkDevice vkDevice = vulkanRenderer.getContext().getVkDevice(); for (uint32_t i = 0; i < mSwapchainImageCount; ++i) { vkDestroyImageView(vkDevice, mSwapChainBuffer[i].view, nullptr); } if (VK_NULL_HANDLE != mVkSwapchainKHR) { vkDestroySwapchainKHR(vkDevice, mVkSwapchainKHR, nullptr); } vkDestroySurfaceKHR(vulkanRenderer.getVulkanRuntimeLinking().getVkInstance(), mVkSurfaceKHR, nullptr); } }
void cleanup() { vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyRenderPass(device, renderPass, nullptr); for (auto imageView : swapChainImageViews) { vkDestroyImageView(device, imageView, nullptr); } vkDestroySwapchainKHR(device, swapChain, nullptr); vkDestroyDevice(device, nullptr); DestroyDebugReportCallbackEXT(instance, callback, nullptr); vkDestroySurfaceKHR(instance, surface, nullptr); vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); glfwTerminate(); }
void _agpu_swap_chain::lostReferences() { if (graphicsQueue) graphicsQueue->release(); if (presentationQueue) presentationQueue->release(); for (auto fb : framebuffers) { if(fb) fb->release(); } for(auto semaphore : semaphores) { if(semaphore) vkDestroySemaphore(device->device, semaphore, nullptr); } if (handle) vkDestroySwapchainKHR(device->device, handle, nullptr); if(surface) vkDestroySurfaceKHR(device->vulkanInstance, surface, nullptr); }
void Window::_DeInitSwapchain() { vkDestroySwapchainKHR( _renderer->GetVulkanDevice(), _swapchain, nullptr ); }
int XdevLSwapChainVulkan::destroy() { vkDestroySwapchainKHR(m_device, m_swapChain, nullptr); return 0; }
static void resize_vulkan(GLFWwindow* /*window*/, int w, int h) { VkResult err; VkSwapchainKHR old_swapchain = g_Swapchain; err = vkDeviceWaitIdle(g_Device); check_vk_result(err); // Destroy old Framebuffer: for (uint32_t i = 0; i < g_BackBufferCount; i++) if (g_BackBufferView[i]) vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); for (uint32_t i = 0; i < g_BackBufferCount; i++) if (g_Framebuffer[i]) vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); if (g_RenderPass) vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); // Create Swapchain: { VkSwapchainCreateInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; info.surface = g_Surface; info.imageFormat = g_SurfaceFormat.format; info.imageColorSpace = g_SurfaceFormat.colorSpace; info.imageArrayLayers = 1; info.imageUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; info.presentMode = g_PresentMode; info.clipped = VK_TRUE; info.oldSwapchain = old_swapchain; VkSurfaceCapabilitiesKHR cap; err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_Gpu, g_Surface, &cap); check_vk_result(err); if (cap.maxImageCount > 0) info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; else info.minImageCount = cap.minImageCount + 2; if (cap.currentExtent.width == 0xffffffff) { fb_width = w; fb_height = h; info.imageExtent.width = fb_width; info.imageExtent.height = fb_height; } else { fb_width = cap.currentExtent.width; fb_height = cap.currentExtent.height; info.imageExtent.width = fb_width; info.imageExtent.height = fb_height; } err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &g_Swapchain); check_vk_result(err); err = vkGetSwapchainImagesKHR(g_Device, g_Swapchain, &g_BackBufferCount, NULL); check_vk_result(err); err = vkGetSwapchainImagesKHR(g_Device, g_Swapchain, &g_BackBufferCount, g_BackBuffer); check_vk_result(err); } if (old_swapchain) vkDestroySwapchainKHR(g_Device, old_swapchain, g_Allocator); // Create the Render Pass: { VkAttachmentDescription attachment = {}; attachment.format = g_SurfaceFormat.format; attachment.samples = VK_SAMPLE_COUNT_1_BIT; attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; VkAttachmentReference color_attachment = {}; color_attachment.attachment = 0; color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_attachment; VkRenderPassCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; info.attachmentCount = 1; info.pAttachments = &attachment; info.subpassCount = 1; info.pSubpasses = &subpass; err = vkCreateRenderPass(g_Device, &info, g_Allocator, &g_RenderPass); check_vk_result(err); } // Create The Image Views { VkImageViewCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = g_SurfaceFormat.format; info.components.r = VK_COMPONENT_SWIZZLE_R; info.components.g = VK_COMPONENT_SWIZZLE_G; info.components.b = VK_COMPONENT_SWIZZLE_B; info.components.a = VK_COMPONENT_SWIZZLE_A; info.subresourceRange = g_ImageRange; for (uint32_t i = 0; i < g_BackBufferCount; i++) { info.image = g_BackBuffer[i]; err = vkCreateImageView(g_Device, &info, g_Allocator, &g_BackBufferView[i]); check_vk_result(err); } } // Create Framebuffer: { VkImageView attachment[1]; VkFramebufferCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; info.renderPass = g_RenderPass; info.attachmentCount = 1; info.pAttachments = attachment; info.width = fb_width; info.height = fb_height; info.layers = 1; for (uint32_t i = 0; i < g_BackBufferCount; i++) { attachment[0] = g_BackBufferView[i]; err = vkCreateFramebuffer(g_Device, &info, g_Allocator, &g_Framebuffer[i]); check_vk_result(err); } } }
void createSwapChainAndImages(VulkanContext& context, VulkanSurfaceContext& surfaceContext) { // Pick an image count and format. According to section 30.5 of VK 1.1, maxImageCount of zero // apparently means "that there is no limit on the number of images, though there may be limits // related to the total amount of memory used by presentable images." uint32_t desiredImageCount = 2; const uint32_t maxImageCount = surfaceContext.surfaceCapabilities.maxImageCount; if (desiredImageCount < surfaceContext.surfaceCapabilities.minImageCount || (maxImageCount != 0 && desiredImageCount > maxImageCount)) { utils::slog.e << "Swap chain does not support " << desiredImageCount << " images.\n"; desiredImageCount = surfaceContext.surfaceCapabilities.minImageCount; } surfaceContext.surfaceFormat = surfaceContext.surfaceFormats[0]; for (const VkSurfaceFormatKHR& format : surfaceContext.surfaceFormats) { if (format.format == VK_FORMAT_R8G8B8A8_UNORM) { surfaceContext.surfaceFormat = format; break; } } const auto compositionCaps = surfaceContext.surfaceCapabilities.supportedCompositeAlpha; const auto compositeAlpha = (compositionCaps & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // Create the low-level swap chain. const auto size = surfaceContext.surfaceCapabilities.currentExtent; VkSwapchainCreateInfoKHR createInfo { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .surface = surfaceContext.surface, .minImageCount = desiredImageCount, .imageFormat = surfaceContext.surfaceFormat.format, .imageColorSpace = surfaceContext.surfaceFormat.colorSpace, .imageExtent = size, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, .compositeAlpha = compositeAlpha, .presentMode = VK_PRESENT_MODE_FIFO_KHR, .clipped = VK_TRUE }; VkSwapchainKHR swapchain; VkResult result = vkCreateSwapchainKHR(context.device, &createInfo, VKALLOC, &swapchain); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkGetPhysicalDeviceSurfaceFormatsKHR error."); surfaceContext.swapchain = swapchain; // Extract the VkImage handles from the swap chain. uint32_t imageCount; result = vkGetSwapchainImagesKHR(context.device, swapchain, &imageCount, nullptr); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkGetSwapchainImagesKHR count error."); surfaceContext.swapContexts.resize(imageCount); std::vector<VkImage> images(imageCount); result = vkGetSwapchainImagesKHR(context.device, swapchain, &imageCount, images.data()); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkGetSwapchainImagesKHR error."); for (size_t i = 0; i < images.size(); ++i) { surfaceContext.swapContexts[i].attachment = { .image = images[i], .format = surfaceContext.surfaceFormat.format }; } utils::slog.i << "vkCreateSwapchain" << ": " << size.width << "x" << size.height << ", " << surfaceContext.surfaceFormat.format << ", " << surfaceContext.surfaceFormat.colorSpace << ", " << imageCount << utils::io::endl; // Create image views. VkImageViewCreateInfo ivCreateInfo = {}; ivCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; ivCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; ivCreateInfo.format = surfaceContext.surfaceFormat.format; ivCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ivCreateInfo.subresourceRange.levelCount = 1; ivCreateInfo.subresourceRange.layerCount = 1; for (size_t i = 0; i < images.size(); ++i) { ivCreateInfo.image = images[i]; result = vkCreateImageView(context.device, &ivCreateInfo, VKALLOC, &surfaceContext.swapContexts[i].attachment.view); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateImageView error."); } createSemaphore(context.device, &surfaceContext.imageAvailable); createSemaphore(context.device, &surfaceContext.renderingFinished); surfaceContext.depth = {}; } void createDepthBuffer(VulkanContext& context, VulkanSurfaceContext& surfaceContext, VkFormat depthFormat) { assert(context.cmdbuffer); // Create an appropriately-sized device-only VkImage. const auto size = surfaceContext.surfaceCapabilities.currentExtent; VkImage depthImage; VkImageCreateInfo imageInfo { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .extent = { size.width, size.height, 1 }, .format = depthFormat, .mipLevels = 1, .arrayLayers = 1, .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, .samples = VK_SAMPLE_COUNT_1_BIT, }; VkResult error = vkCreateImage(context.device, &imageInfo, VKALLOC, &depthImage); ASSERT_POSTCONDITION(!error, "Unable to create depth image."); // Allocate memory for the VkImage and bind it. VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(context.device, depthImage, &memReqs); VkMemoryAllocateInfo allocInfo { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = memReqs.size, .memoryTypeIndex = selectMemoryType(context, memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) }; error = vkAllocateMemory(context.device, &allocInfo, nullptr, &surfaceContext.depth.memory); ASSERT_POSTCONDITION(!error, "Unable to allocate depth memory."); error = vkBindImageMemory(context.device, depthImage, surfaceContext.depth.memory, 0); ASSERT_POSTCONDITION(!error, "Unable to bind depth memory."); // Create a VkImageView so that we can attach depth to the framebuffer. VkImageView depthView; VkImageViewCreateInfo viewInfo { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = depthImage, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = depthFormat, .subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, .subresourceRange.levelCount = 1, .subresourceRange.layerCount = 1, }; error = vkCreateImageView(context.device, &viewInfo, VKALLOC, &depthView); ASSERT_POSTCONDITION(!error, "Unable to create depth view."); // Transition the depth image into an optimal layout. VkImageMemoryBarrier barrier { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = depthImage, .subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, .subresourceRange.levelCount = 1, .subresourceRange.layerCount = 1, .dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT }; vkCmdPipelineBarrier(context.cmdbuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); // Go ahead and set the depth attachment fields, which serves as a signal to VulkanDriver that // it is now ready. surfaceContext.depth.view = depthView; surfaceContext.depth.image = depthImage; surfaceContext.depth.format = depthFormat; } void transitionDepthBuffer(VulkanContext& context, VulkanSurfaceContext& sc, VkFormat depthFormat) { // Begin a new command buffer solely for the purpose of transitioning the image layout. SwapContext& swap = getSwapContext(context); VkResult result = vkWaitForFences(context.device, 1, &swap.fence, VK_FALSE, UINT64_MAX); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkWaitForFences error."); result = vkResetFences(context.device, 1, &swap.fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkResetFences error."); VkCommandBuffer cmdbuffer = swap.cmdbuffer; result = vkResetCommandBuffer(cmdbuffer, 0); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkResetCommandBuffer error."); VkCommandBufferBeginInfo beginInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, }; result = vkBeginCommandBuffer(cmdbuffer, &beginInfo); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkBeginCommandBuffer error."); context.cmdbuffer = cmdbuffer; // Create the depth buffer and issue a pipeline barrier command. createDepthBuffer(context, sc, depthFormat); // Flush the command buffer. result = vkEndCommandBuffer(context.cmdbuffer); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkEndCommandBuffer error."); context.cmdbuffer = nullptr; VkSubmitInfo submitInfo { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &swap.cmdbuffer, }; result = vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, swap.fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkQueueSubmit error."); swap.submitted = false; } void createCommandBuffersAndFences(VulkanContext& context, VulkanSurfaceContext& surfaceContext) { // Allocate command buffers. VkCommandBufferAllocateInfo allocateInfo = {}; allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocateInfo.commandPool = context.commandPool; allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocateInfo.commandBufferCount = (uint32_t) surfaceContext.swapContexts.size(); std::vector<VkCommandBuffer> cmdbufs(allocateInfo.commandBufferCount); VkResult result = vkAllocateCommandBuffers(context.device, &allocateInfo, cmdbufs.data()); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkAllocateCommandBuffers error."); for (uint32_t i = 0; i < allocateInfo.commandBufferCount; ++i) { surfaceContext.swapContexts[i].cmdbuffer = cmdbufs[i]; } // Create fences. VkFenceCreateInfo fenceCreateInfo = {}; fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; for (uint32_t i = 0; i < allocateInfo.commandBufferCount; i++) { result = vkCreateFence(context.device, &fenceCreateInfo, VKALLOC, &surfaceContext.swapContexts[i].fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateFence error."); } } void destroySurfaceContext(VulkanContext& context, VulkanSurfaceContext& surfaceContext) { for (SwapContext& swapContext : surfaceContext.swapContexts) { vkFreeCommandBuffers(context.device, context.commandPool, 1, &swapContext.cmdbuffer); vkDestroyFence(context.device, swapContext.fence, VKALLOC); vkDestroyImageView(context.device, swapContext.attachment.view, VKALLOC); swapContext.fence = VK_NULL_HANDLE; swapContext.attachment.view = VK_NULL_HANDLE; } vkDestroySwapchainKHR(context.device, surfaceContext.swapchain, VKALLOC); vkDestroySemaphore(context.device, surfaceContext.imageAvailable, VKALLOC); vkDestroySemaphore(context.device, surfaceContext.renderingFinished, VKALLOC); vkDestroySurfaceKHR(context.instance, surfaceContext.surface, VKALLOC); vkDestroyImageView(context.device, surfaceContext.depth.view, VKALLOC); vkDestroyImage(context.device, surfaceContext.depth.image, VKALLOC); vkFreeMemory(context.device, surfaceContext.depth.memory, VKALLOC); if (context.currentSurface == &surfaceContext) { context.currentSurface = nullptr; } } uint32_t selectMemoryType(VulkanContext& context, uint32_t flags, VkFlags reqs) { for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) { if (flags & 1) { if ((context.memoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) { return i; } } flags >>= 1; } ASSERT_POSTCONDITION(false, "Unable to find a memory type that meets requirements."); return (uint32_t) ~0ul; } VkFormat getVkFormat(ElementType type, bool normalized) { using ElementType = ElementType; if (normalized) { switch (type) { // Single Component Types case ElementType::BYTE: return VK_FORMAT_R8_SNORM; case ElementType::UBYTE: return VK_FORMAT_R8_UNORM; case ElementType::SHORT: return VK_FORMAT_R16_SNORM; case ElementType::USHORT: return VK_FORMAT_R16_UNORM; // Two Component Types case ElementType::BYTE2: return VK_FORMAT_R8G8_SNORM; case ElementType::UBYTE2: return VK_FORMAT_R8G8_UNORM; case ElementType::SHORT2: return VK_FORMAT_R16G16_SNORM; case ElementType::USHORT2: return VK_FORMAT_R16G16_UNORM; // Three Component Types case ElementType::BYTE3: return VK_FORMAT_R8G8B8_SNORM; case ElementType::UBYTE3: return VK_FORMAT_R8G8B8_UNORM; case ElementType::SHORT3: return VK_FORMAT_R16G16B16_SNORM; case ElementType::USHORT3: return VK_FORMAT_R16G16B16_UNORM; // Four Component Types case ElementType::BYTE4: return VK_FORMAT_R8G8B8A8_SNORM; case ElementType::UBYTE4: return VK_FORMAT_R8G8B8A8_UNORM; case ElementType::SHORT4: return VK_FORMAT_R16G16B16A16_SNORM; case ElementType::USHORT4: return VK_FORMAT_R16G16B16A16_UNORM; default: ASSERT_POSTCONDITION(false, "Normalized format does not exist."); return VK_FORMAT_UNDEFINED; } } switch (type) { // Single Component Types case ElementType::BYTE: return VK_FORMAT_R8_SINT; case ElementType::UBYTE: return VK_FORMAT_R8_UINT; case ElementType::SHORT: return VK_FORMAT_R16_SINT; case ElementType::USHORT: return VK_FORMAT_R16_UINT; case ElementType::HALF: return VK_FORMAT_R16_SFLOAT; case ElementType::INT: return VK_FORMAT_R32_SINT; case ElementType::UINT: return VK_FORMAT_R32_UINT; case ElementType::FLOAT: return VK_FORMAT_R32_SFLOAT; // Two Component Types case ElementType::BYTE2: return VK_FORMAT_R8G8_SINT; case ElementType::UBYTE2: return VK_FORMAT_R8G8_UINT; case ElementType::SHORT2: return VK_FORMAT_R16G16_SINT; case ElementType::USHORT2: return VK_FORMAT_R16G16_UINT; case ElementType::HALF2: return VK_FORMAT_R16G16_SFLOAT; case ElementType::FLOAT2: return VK_FORMAT_R32G32_SFLOAT; // Three Component Types case ElementType::BYTE3: return VK_FORMAT_R8G8B8_SINT; case ElementType::UBYTE3: return VK_FORMAT_R8G8B8_UINT; case ElementType::SHORT3: return VK_FORMAT_R16G16B16_SINT; case ElementType::USHORT3: return VK_FORMAT_R16G16B16_UINT; case ElementType::HALF3: return VK_FORMAT_R16G16B16_SFLOAT; case ElementType::FLOAT3: return VK_FORMAT_R32G32B32_SFLOAT; // Four Component Types case ElementType::BYTE4: return VK_FORMAT_R8G8B8A8_SINT; case ElementType::UBYTE4: return VK_FORMAT_R8G8B8A8_UINT; case ElementType::SHORT4: return VK_FORMAT_R16G16B16A16_SINT; case ElementType::USHORT4: return VK_FORMAT_R16G16B16A16_UINT; case ElementType::HALF4: return VK_FORMAT_R16G16B16A16_SFLOAT; case ElementType::FLOAT4: return VK_FORMAT_R32G32B32A32_SFLOAT; } return VK_FORMAT_UNDEFINED; } VkFormat getVkFormat(TextureFormat format) { using TextureFormat = TextureFormat; switch (format) { // 8 bits per element. case TextureFormat::R8: return VK_FORMAT_R8_UNORM; case TextureFormat::R8_SNORM: return VK_FORMAT_R8_SNORM; case TextureFormat::R8UI: return VK_FORMAT_R8_UINT; case TextureFormat::R8I: return VK_FORMAT_R8_SINT; case TextureFormat::STENCIL8: return VK_FORMAT_S8_UINT; // 16 bits per element. case TextureFormat::R16F: return VK_FORMAT_R16_SFLOAT; case TextureFormat::R16UI: return VK_FORMAT_R16_UINT; case TextureFormat::R16I: return VK_FORMAT_R16_SINT; case TextureFormat::RG8: return VK_FORMAT_R8G8_UNORM; case TextureFormat::RG8_SNORM: return VK_FORMAT_R8G8_SNORM; case TextureFormat::RG8UI: return VK_FORMAT_R8G8_UINT; case TextureFormat::RG8I: return VK_FORMAT_R8G8_SINT; case TextureFormat::RGB565: return VK_FORMAT_R5G6B5_UNORM_PACK16; case TextureFormat::RGB5_A1: return VK_FORMAT_R5G5B5A1_UNORM_PACK16; case TextureFormat::RGBA4: return VK_FORMAT_R4G4B4A4_UNORM_PACK16; case TextureFormat::DEPTH16: return VK_FORMAT_D16_UNORM; // 24 bits per element. In practice, very few GPU vendors support these. For simplicity // we just assume they are not supported, not bothering to query the device capabilities. // Note that VK_FORMAT_ enums for 24-bit formats exist, but are meant for vertex attributes. case TextureFormat::RGB8: case TextureFormat::SRGB8: case TextureFormat::RGB8_SNORM: case TextureFormat::RGB8UI: case TextureFormat::RGB8I: case TextureFormat::DEPTH24: return VK_FORMAT_UNDEFINED; // 32 bits per element. case TextureFormat::R32F: return VK_FORMAT_R32_SFLOAT; case TextureFormat::R32UI: return VK_FORMAT_R32_UINT; case TextureFormat::R32I: return VK_FORMAT_R32_SINT; case TextureFormat::RG16F: return VK_FORMAT_R16G16_SFLOAT; case TextureFormat::RG16UI: return VK_FORMAT_R16G16_UINT; case TextureFormat::RG16I: return VK_FORMAT_R16G16_SINT; case TextureFormat::R11F_G11F_B10F: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; case TextureFormat::RGB9_E5: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; case TextureFormat::RGBA8: return VK_FORMAT_R8G8B8A8_UNORM; case TextureFormat::SRGB8_A8: return VK_FORMAT_R8G8B8A8_SRGB; case TextureFormat::RGBA8_SNORM: return VK_FORMAT_R8G8B8A8_SNORM; case TextureFormat::RGBM: return VK_FORMAT_R8G8B8A8_UNORM; case TextureFormat::RGB10_A2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; case TextureFormat::RGBA8UI: return VK_FORMAT_R8G8B8A8_UINT; case TextureFormat::RGBA8I: return VK_FORMAT_R8G8B8A8_SINT; case TextureFormat::DEPTH32F: return VK_FORMAT_D32_SFLOAT; case TextureFormat::DEPTH24_STENCIL8: return VK_FORMAT_D24_UNORM_S8_UINT; case TextureFormat::DEPTH32F_STENCIL8: return VK_FORMAT_D32_SFLOAT_S8_UINT; // 48 bits per element. Note that many GPU vendors do not support these. case TextureFormat::RGB16F: return VK_FORMAT_R16G16B16_SFLOAT; case TextureFormat::RGB16UI: return VK_FORMAT_R16G16B16_UINT; case TextureFormat::RGB16I: return VK_FORMAT_R16G16B16_SINT; // 64 bits per element. case TextureFormat::RG32F: return VK_FORMAT_R32G32_SFLOAT; case TextureFormat::RG32UI: return VK_FORMAT_R32G32_UINT; case TextureFormat::RG32I: return VK_FORMAT_R32G32_SINT; case TextureFormat::RGBA16F: return VK_FORMAT_R16G16B16A16_SFLOAT; case TextureFormat::RGBA16UI: return VK_FORMAT_R16G16B16A16_UINT; case TextureFormat::RGBA16I: return VK_FORMAT_R16G16B16A16_SINT; // 96-bits per element. case TextureFormat::RGB32F: return VK_FORMAT_R32G32B32_SFLOAT; case TextureFormat::RGB32UI: return VK_FORMAT_R32G32B32_UINT; case TextureFormat::RGB32I: return VK_FORMAT_R32G32B32_SINT; // 128-bits per element case TextureFormat::RGBA32F: return VK_FORMAT_R32G32B32A32_SFLOAT; case TextureFormat::RGBA32UI: return VK_FORMAT_R32G32B32A32_UINT; case TextureFormat::RGBA32I: return VK_FORMAT_R32G32B32A32_SINT; default: return VK_FORMAT_UNDEFINED; } } uint32_t getBytesPerPixel(TextureFormat format) { return details::FTexture::getFormatSize(format); } // See also FTexture::computeTextureDataSize, which takes a public-facing Texture format rather // than a driver-level Texture format, and can account for a specified byte alignment. uint32_t computeSize(TextureFormat format, uint32_t w, uint32_t h, uint32_t d) { const size_t bytesPerTexel = details::FTexture::getFormatSize(format); return bytesPerTexel * w * h * d; } SwapContext& getSwapContext(VulkanContext& context) { VulkanSurfaceContext& surface = *context.currentSurface; return surface.swapContexts[surface.currentSwapIndex]; } bool hasPendingWork(VulkanContext& context) { if (context.pendingWork.size() > 0) { return true; } if (context.currentSurface) { for (auto& swapContext : context.currentSurface->swapContexts) { if (swapContext.pendingWork.size() > 0) { return true; } } } return false; } VkCompareOp getCompareOp(SamplerCompareFunc func) { using Compare = driver::SamplerCompareFunc; switch (func) { case Compare::LE: return VK_COMPARE_OP_LESS_OR_EQUAL; case Compare::GE: return VK_COMPARE_OP_GREATER_OR_EQUAL; case Compare::L: return VK_COMPARE_OP_LESS; case Compare::G: return VK_COMPARE_OP_GREATER; case Compare::E: return VK_COMPARE_OP_EQUAL; case Compare::NE: return VK_COMPARE_OP_NOT_EQUAL; case Compare::A: return VK_COMPARE_OP_ALWAYS; case Compare::N: return VK_COMPARE_OP_NEVER; } } VkBlendFactor getBlendFactor(BlendFunction mode) { using BlendFunction = filament::driver::BlendFunction; switch (mode) { case BlendFunction::ZERO: return VK_BLEND_FACTOR_ZERO; case BlendFunction::ONE: return VK_BLEND_FACTOR_ONE; case BlendFunction::SRC_COLOR: return VK_BLEND_FACTOR_SRC_COLOR; case BlendFunction::ONE_MINUS_SRC_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; case BlendFunction::DST_COLOR: return VK_BLEND_FACTOR_DST_COLOR; case BlendFunction::ONE_MINUS_DST_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; case BlendFunction::SRC_ALPHA: return VK_BLEND_FACTOR_SRC_ALPHA; case BlendFunction::ONE_MINUS_SRC_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; case BlendFunction::DST_ALPHA: return VK_BLEND_FACTOR_DST_ALPHA; case BlendFunction::ONE_MINUS_DST_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; case BlendFunction::SRC_ALPHA_SATURATE: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; } } void waitForIdle(VulkanContext& context) { // If there's no valid GPU then we have nothing to do. if (!context.device) { return; } // If there's no surface, then there's no command buffer. if (!context.currentSurface) { return; } // First, wait for submitted command buffer(s) to finish. VkFence fences[2]; uint32_t nfences = 0; auto& surfaceContext = *context.currentSurface; for (auto& swapContext : surfaceContext.swapContexts) { assert(nfences < 2); if (swapContext.submitted && swapContext.fence) { fences[nfences++] = swapContext.fence; swapContext.submitted = false; } } if (nfences > 0) { vkWaitForFences(context.device, nfences, fences, VK_FALSE, ~0ull); } // If we don't have any pending work, we're done. if (!hasPendingWork(context)) { return; } // We cannot invoke arbitrary commands inside a render pass. assert(context.currentRenderPass.renderPass == VK_NULL_HANDLE); // Create a one-off command buffer to avoid the cost of swap chain acquisition and to avoid // the possibility of SURFACE_LOST. Note that Vulkan command buffers use the Allocate/Free // model instead of Create/Destroy and are therefore okay to create at a high frequency. VkCommandBuffer cmdbuffer; VkFence fence; VkCommandBufferBeginInfo beginInfo { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; VkCommandBufferAllocateInfo allocateInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = context.commandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1 }; VkFenceCreateInfo fenceCreateInfo { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, }; vkAllocateCommandBuffers(context.device, &allocateInfo, &cmdbuffer); vkCreateFence(context.device, &fenceCreateInfo, VKALLOC, &fence); // Keep performing work until there's nothing queued up. This should never iterate more than // a couple times because the only work we queue up is for resource transition / reclamation. VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; VkSubmitInfo submitInfo { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pWaitDstStageMask = &waitDestStageMask, .commandBufferCount = 1, .pCommandBuffers = &cmdbuffer, }; int cycles = 0; while (hasPendingWork(context)) { if (cycles++ > 2) { utils::slog.e << "Unexpected daisychaining of pending work." << utils::io::endl; break; } for (auto& swapContext : context.currentSurface->swapContexts) { vkBeginCommandBuffer(cmdbuffer, &beginInfo); performPendingWork(context, swapContext, cmdbuffer); vkEndCommandBuffer(cmdbuffer); vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, fence); vkWaitForFences(context.device, 1, &fence, VK_FALSE, UINT64_MAX); vkResetFences(context.device, 1, &fence); vkResetCommandBuffer(cmdbuffer, 0); } } vkFreeCommandBuffers(context.device, context.commandPool, 1, &cmdbuffer); vkDestroyFence(context.device, fence, VKALLOC); } void acquireCommandBuffer(VulkanContext& context) { // Ask Vulkan for the next image in the swap chain and update the currentSwapIndex. VulkanSurfaceContext& surface = *context.currentSurface; VkResult result = vkAcquireNextImageKHR(context.device, surface.swapchain, UINT64_MAX, surface.imageAvailable, VK_NULL_HANDLE, &surface.currentSwapIndex); ASSERT_POSTCONDITION(result != VK_ERROR_OUT_OF_DATE_KHR, "Stale / resized swap chain not yet supported."); ASSERT_POSTCONDITION(result == VK_SUBOPTIMAL_KHR || result == VK_SUCCESS, "vkAcquireNextImageKHR error."); SwapContext& swap = getSwapContext(context); // Ensure that the previous submission of this command buffer has finished. result = vkWaitForFences(context.device, 1, &swap.fence, VK_FALSE, UINT64_MAX); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkWaitForFences error."); // Restart the command buffer. result = vkResetFences(context.device, 1, &swap.fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkResetFences error."); VkCommandBuffer cmdbuffer = swap.cmdbuffer; VkResult error = vkResetCommandBuffer(cmdbuffer, 0); ASSERT_POSTCONDITION(not error, "vkResetCommandBuffer error."); VkCommandBufferBeginInfo beginInfo { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, }; error = vkBeginCommandBuffer(cmdbuffer, &beginInfo); ASSERT_POSTCONDITION(not error, "vkBeginCommandBuffer error."); context.cmdbuffer = cmdbuffer; swap.submitted = false; } void releaseCommandBuffer(VulkanContext& context) { // Finalize the command buffer and set the cmdbuffer pointer to null. VkResult result = vkEndCommandBuffer(context.cmdbuffer); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkEndCommandBuffer error."); context.cmdbuffer = nullptr; // Submit the command buffer. VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; VulkanSurfaceContext& surfaceContext = *context.currentSurface; SwapContext& swapContext = getSwapContext(context); VkSubmitInfo submitInfo { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1u, .pWaitSemaphores = &surfaceContext.imageAvailable, .pWaitDstStageMask = &waitDestStageMask, .commandBufferCount = 1, .pCommandBuffers = &swapContext.cmdbuffer, .signalSemaphoreCount = 1u, .pSignalSemaphores = &surfaceContext.renderingFinished, }; result = vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, swapContext.fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkQueueSubmit error."); swapContext.submitted = true; } void performPendingWork(VulkanContext& context, SwapContext& swapContext, VkCommandBuffer cmdbuf) { // First, execute pending tasks that are specific to this swap context. Copy the tasks into a // local queue first, which allows newly added tasks to be deferred until the next frame. decltype(swapContext.pendingWork) tasks; tasks.swap(swapContext.pendingWork); for (auto& callback : tasks) { callback(cmdbuf); } // Next, execute the global pending work. Again, we copy the work queue into a local queue // to allow tasks to re-add themselves. tasks.clear(); tasks.swap(context.pendingWork); for (auto& callback : tasks) { callback(cmdbuf); } } // Flushes the command buffer and waits for it to finish executing. Useful for diagnosing // sychronization issues. void flushCommandBuffer(VulkanContext& context) { VulkanSurfaceContext& surface = *context.currentSurface; const SwapContext& sc = surface.swapContexts[surface.currentSwapIndex]; // Submit the command buffer. VkResult error = vkEndCommandBuffer(context.cmdbuffer); ASSERT_POSTCONDITION(!error, "vkEndCommandBuffer error."); VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; VkSubmitInfo submitInfo { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pWaitDstStageMask = &waitDestStageMask, .commandBufferCount = 1, .pCommandBuffers = &context.cmdbuffer, }; error = vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, sc.fence); ASSERT_POSTCONDITION(!error, "vkQueueSubmit error."); // Restart the command buffer. error = vkWaitForFences(context.device, 1, &sc.fence, VK_FALSE, UINT64_MAX); ASSERT_POSTCONDITION(!error, "vkWaitForFences error."); error = vkResetFences(context.device, 1, &sc.fence); ASSERT_POSTCONDITION(!error, "vkResetFences error."); error = vkResetCommandBuffer(context.cmdbuffer, 0); ASSERT_POSTCONDITION(!error, "vkResetCommandBuffer error."); VkCommandBufferBeginInfo beginInfo { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, }; error = vkBeginCommandBuffer(context.cmdbuffer, &beginInfo); ASSERT_POSTCONDITION(!error, "vkBeginCommandBuffer error."); } VkFormat findSupportedFormat(VulkanContext& context, const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { for (VkFormat format : candidates) { VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(context.physicalDevice, format, &props); if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) { return format; } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) { return format; } } return VK_FORMAT_UNDEFINED; } } // namespace filament } // namespace driver
SurfaceWindow::~SurfaceWindow() { vkDestroySwapchainKHR(mDevice, mSwapchain, nullptr); vkDestroySurfaceKHR(mInstance, mSurface, nullptr); }
bool VulkanCommon::CreateSwapChain() { if( Vulkan.Device != VK_NULL_HANDLE ) { vkDeviceWaitIdle( Vulkan.Device ); } for( size_t i = 0; i < Vulkan.SwapChain.Images.size(); ++i ) { if( Vulkan.SwapChain.Images[i].ImageView != VK_NULL_HANDLE ) { vkDestroyImageView( GetDevice(), Vulkan.SwapChain.Images[i].ImageView, nullptr ); Vulkan.SwapChain.Images[i].ImageView = VK_NULL_HANDLE; } } Vulkan.SwapChain.Images.clear(); VkSurfaceCapabilitiesKHR surface_capabilities; if( vkGetPhysicalDeviceSurfaceCapabilitiesKHR( Vulkan.PhysicalDevice, Vulkan.PresentationSurface, &surface_capabilities ) != VK_SUCCESS ) { std::cout << "Could not check presentation surface capabilities!" << std::endl; return false; } uint32_t formats_count; if( (vkGetPhysicalDeviceSurfaceFormatsKHR( Vulkan.PhysicalDevice, Vulkan.PresentationSurface, &formats_count, nullptr ) != VK_SUCCESS) || (formats_count == 0) ) { std::cout << "Error occurred during presentation surface formats enumeration!" << std::endl; return false; } std::vector<VkSurfaceFormatKHR> surface_formats( formats_count ); if( vkGetPhysicalDeviceSurfaceFormatsKHR( Vulkan.PhysicalDevice, Vulkan.PresentationSurface, &formats_count, &surface_formats[0] ) != VK_SUCCESS ) { std::cout << "Error occurred during presentation surface formats enumeration!" << std::endl; return false; } uint32_t present_modes_count; if( (vkGetPhysicalDeviceSurfacePresentModesKHR( Vulkan.PhysicalDevice, Vulkan.PresentationSurface, &present_modes_count, nullptr ) != VK_SUCCESS) || (present_modes_count == 0) ) { std::cout << "Error occurred during presentation surface present modes enumeration!" << std::endl; return false; } std::vector<VkPresentModeKHR> present_modes( present_modes_count ); if( vkGetPhysicalDeviceSurfacePresentModesKHR( Vulkan.PhysicalDevice, Vulkan.PresentationSurface, &present_modes_count, &present_modes[0] ) != VK_SUCCESS ) { std::cout << "Error occurred during presentation surface present modes enumeration!" << std::endl; return false; } uint32_t desired_number_of_images = GetSwapChainNumImages( surface_capabilities ); VkSurfaceFormatKHR desired_format = GetSwapChainFormat( surface_formats ); VkExtent2D desired_extent = GetSwapChainExtent( surface_capabilities ); VkImageUsageFlags desired_usage = GetSwapChainUsageFlags( surface_capabilities ); VkSurfaceTransformFlagBitsKHR desired_transform = GetSwapChainTransform( surface_capabilities ); VkPresentModeKHR desired_present_mode = GetSwapChainPresentMode( present_modes ); VkSwapchainKHR old_swap_chain = Vulkan.SwapChain.Handle; if( static_cast<int>(desired_usage) == -1 ) { return false; } if( static_cast<int>(desired_present_mode) == -1 ) { return false; } VkSwapchainCreateInfoKHR swap_chain_create_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, // VkStructureType sType nullptr, // const void *pNext 0, // VkSwapchainCreateFlagsKHR flags Vulkan.PresentationSurface, // VkSurfaceKHR surface desired_number_of_images, // uint32_t minImageCount desired_format.format, // VkFormat imageFormat desired_format.colorSpace, // VkColorSpaceKHR imageColorSpace desired_extent, // VkExtent2D imageExtent 1, // uint32_t imageArrayLayers desired_usage, // VkImageUsageFlags imageUsage VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode imageSharingMode 0, // uint32_t queueFamilyIndexCount nullptr, // const uint32_t *pQueueFamilyIndices desired_transform, // VkSurfaceTransformFlagBitsKHR preTransform VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, // VkCompositeAlphaFlagBitsKHR compositeAlpha desired_present_mode, // VkPresentModeKHR presentMode VK_TRUE, // VkBool32 clipped old_swap_chain // VkSwapchainKHR oldSwapchain }; if( vkCreateSwapchainKHR( Vulkan.Device, &swap_chain_create_info, nullptr, &Vulkan.SwapChain.Handle ) != VK_SUCCESS ) { std::cout << "Could not create swap chain!" << std::endl; return false; } if( old_swap_chain != VK_NULL_HANDLE ) { vkDestroySwapchainKHR( Vulkan.Device, old_swap_chain, nullptr ); } Vulkan.SwapChain.Format = desired_format.format; uint32_t image_count = 0; if( (vkGetSwapchainImagesKHR( Vulkan.Device, Vulkan.SwapChain.Handle, &image_count, nullptr ) != VK_SUCCESS) || (image_count == 0) ) { std::cout << "Could not get swap chain images!" << std::endl; return false; } Vulkan.SwapChain.Images.resize( image_count ); std::vector<VkImage> images( image_count ); if( vkGetSwapchainImagesKHR( Vulkan.Device, Vulkan.SwapChain.Handle, &image_count, &images[0] ) != VK_SUCCESS ) { std::cout << "Could not get swap chain images!" << std::endl; return false; } for( size_t i = 0; i < Vulkan.SwapChain.Images.size(); ++i ) { Vulkan.SwapChain.Images[i].Handle = images[i]; } Vulkan.SwapChain.Extent = desired_extent; return CreateSwapChainImageViews(); }
bool SwapChain::CreateSwapChain() { // Look up surface properties to determine image count and dimensions VkSurfaceCapabilitiesKHR surface_capabilities; VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_vulkan_context->GetPhysicalDevice(), m_surface, &surface_capabilities); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: "); return false; } // Select swap chain format and present mode if (!SelectSurfaceFormat() || !SelectPresentMode()) return false; // Select number of images in swap chain, we prefer one buffer in the background to work on uint32_t image_count = surface_capabilities.minImageCount + 1; // maxImageCount can be zero, in which case there isn't an upper limit on the number of buffers. if (surface_capabilities.maxImageCount > 0) image_count = std::min(image_count, surface_capabilities.maxImageCount); // Determine the dimensions of the swap chain. Values of -1 indicate the size we specify here // determines window size? VkExtent2D size = surface_capabilities.currentExtent; if (size.width == UINT32_MAX) { size.width = std::min(std::max(surface_capabilities.minImageExtent.width, 640u), surface_capabilities.maxImageExtent.width); size.height = std::min(std::max(surface_capabilities.minImageExtent.height, 480u), surface_capabilities.maxImageExtent.height); } // Prefer identity transform if possible VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; if (!(surface_capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) transform = surface_capabilities.currentTransform; // Select swap chain flags, we only need a colour attachment VkImageUsageFlags image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; if (!(surface_capabilities.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) { ERROR_LOG(VIDEO, "Vulkan: Swap chain does not support usage as color attachment"); return false; } // Store the old/current swap chain when recreating for resize VkSwapchainKHR old_swap_chain = m_swap_chain; // Now we can actually create the swap chain // TODO: Handle case where the present queue is not the graphics queue. VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, nullptr, 0, m_surface, image_count, m_surface_format.format, m_surface_format.colorSpace, size, 1, image_usage, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr, transform, VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, m_present_mode, VK_TRUE, old_swap_chain}; res = vkCreateSwapchainKHR(g_vulkan_context->GetDevice(), &swap_chain_info, nullptr, &m_swap_chain); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateSwapchainKHR failed: "); return false; } // Now destroy the old swap chain, since it's been recreated. // We can do this immediately since all work should have been completed before calling resize. if (old_swap_chain != VK_NULL_HANDLE) vkDestroySwapchainKHR(g_vulkan_context->GetDevice(), old_swap_chain, nullptr); m_width = size.width; m_height = size.height; return true; }
/** * * @ThreadSafe */ ISwapchainSP VKTS_APIENTRY wsiSwapchainCreate(const VkPhysicalDevice physicalDevice, const VkDevice device, const VkSwapchainCreateFlagsKHR flags, const VkSurfaceKHR surface, const uint32_t minImageCount, const uint32_t imageArrayLayers, const VkImageUsageFlags imageUsage, const VkSharingMode imageSharingMode, const uint32_t queueFamilyIndexCount, const uint32_t* queueFamilyIndices, const VkCompositeAlphaFlagBitsKHR compositeAlpha, const VkBool32 clipped, const VkSwapchainKHR oldSwapchain) { if (!physicalDevice || !device) { return ISwapchainSP(); } VkResult result; // VkSurfaceCapabilitiesKHR surfaceCapabilities; result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, "Wsi: Could not get surface properties."); return ISwapchainSP(); } VkSurfaceTransformFlagBitsKHR preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; if (!(surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) { preTransform = surfaceCapabilities.currentTransform; } uint32_t surfacePresentModesCount; result = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &surfacePresentModesCount, nullptr); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, "Wsi: Could not get surface present mode count."); return ISwapchainSP(); } std::unique_ptr<VkPresentModeKHR[]> surfacePresentModes = std::unique_ptr<VkPresentModeKHR[]> (new VkPresentModeKHR[surfacePresentModesCount]); if (!surfacePresentModes.get()) { vkts::logPrint(VKTS_LOG_ERROR, "Wsi: Could not create surface present modes."); return ISwapchainSP(); } result = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &surfacePresentModesCount, &surfacePresentModes[0]); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, "Wsi: Could not get surface present modes."); return ISwapchainSP(); } // Regarding specification, this present mode has to be supported. VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; uint32_t surfaceFormatsCount; result = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatsCount, nullptr); if (result != VK_SUCCESS || surfaceFormatsCount == 0) { vkts::logPrint(VKTS_LOG_ERROR, "Wsi: Could not get surface formats count."); return ISwapchainSP(); } std::unique_ptr<VkSurfaceFormatKHR[]> surfaceFormats = std::unique_ptr <VkSurfaceFormatKHR[]>(new VkSurfaceFormatKHR[surfaceFormatsCount]); if (!surfaceFormats.get()) { vkts::logPrint(VKTS_LOG_ERROR, "Wsi: Could not create surface formats."); return ISwapchainSP(); } result = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatsCount, &surfaceFormats[0]); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, "Wsi: Could not get surface formats."); return ISwapchainSP(); } VkFormat imageFormat = surfaceFormats[0].format; VkColorSpaceKHR imageColorSpace = surfaceFormats[0].colorSpace; VkSwapchainCreateInfoKHR swapchainCreateInfo; memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR)); swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchainCreateInfo.flags = flags; swapchainCreateInfo.surface = surface; swapchainCreateInfo.minImageCount = minImageCount; swapchainCreateInfo.imageFormat = imageFormat; swapchainCreateInfo.imageColorSpace = imageColorSpace; swapchainCreateInfo.imageExtent = surfaceCapabilities.currentExtent; swapchainCreateInfo.imageArrayLayers = imageArrayLayers; swapchainCreateInfo.imageUsage = imageUsage; swapchainCreateInfo.imageSharingMode = imageSharingMode; swapchainCreateInfo.queueFamilyIndexCount = queueFamilyIndexCount; swapchainCreateInfo.pQueueFamilyIndices = queueFamilyIndices; swapchainCreateInfo.preTransform = preTransform; swapchainCreateInfo.compositeAlpha = compositeAlpha; swapchainCreateInfo.presentMode = presentMode; swapchainCreateInfo.clipped = clipped; swapchainCreateInfo.oldSwapchain = oldSwapchain; VkSwapchainKHR swapchain; result = vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain); if (result != VK_SUCCESS) { logPrint(VKTS_LOG_ERROR, "Wsi: Could not create swapchain."); return ISwapchainSP(); } auto newInstance = new Swapchain(device, flags, surface, minImageCount, imageFormat, imageColorSpace, surfaceCapabilities.currentExtent, imageArrayLayers, imageUsage, imageSharingMode, queueFamilyIndexCount, queueFamilyIndices, preTransform, compositeAlpha, presentMode, clipped, oldSwapchain, swapchain); if (!newInstance) { vkDestroySwapchainKHR(device, swapchain, nullptr); return ISwapchainSP(); } return ISwapchainSP(newInstance); }
/** * Good ol' main function. */ int main(int argc, char* argv[]) { static int windowWidth = 800; static int windowHeight = 600; static const char * applicationName = "SdlVulkanDemo_03_double_buffering"; static const char * engineName = applicationName; bool boolResult; VkResult result; /* * SDL2 Initialization */ SDL_Window *mySdlWindow; SDL_SysWMinfo mySdlSysWmInfo; boolResult = vkdemos::utils::sdl2Initialization(applicationName, windowWidth, windowHeight, mySdlWindow, mySdlSysWmInfo); assert(boolResult); /* * Vulkan initialization. */ std::vector<const char *> layersNamesToEnable; layersNamesToEnable.push_back("VK_LAYER_LUNARG_standard_validation"); std::vector<const char *> extensionsNamesToEnable; extensionsNamesToEnable.push_back(VK_KHR_SURFACE_EXTENSION_NAME); extensionsNamesToEnable.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); extensionsNamesToEnable.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); // TODO: add support for other windowing systems VkInstance myInstance; boolResult = vkdemos::createVkInstance(layersNamesToEnable, extensionsNamesToEnable, applicationName, engineName, myInstance); assert(boolResult); VkDebugReportCallbackEXT myDebugReportCallback; vkdemos::createDebugReportCallback(myInstance, VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT, vkdemos::debugCallback, myDebugReportCallback ); VkPhysicalDevice myPhysicalDevice; boolResult = vkdemos::chooseVkPhysicalDevice(myInstance, 0, myPhysicalDevice); assert(boolResult); VkSurfaceKHR mySurface; boolResult = vkdemos::createVkSurface(myInstance, mySdlSysWmInfo, mySurface); assert(boolResult); VkDevice myDevice; VkQueue myQueue; uint32_t myQueueFamilyIndex; boolResult = vkdemos::createVkDeviceAndVkQueue(myPhysicalDevice, mySurface, layersNamesToEnable, myDevice, myQueue, myQueueFamilyIndex); assert(boolResult); VkSwapchainKHR mySwapchain; VkFormat mySurfaceFormat; boolResult = vkdemos::createVkSwapchain(myPhysicalDevice, myDevice, mySurface, windowWidth, windowHeight, FRAME_LAG, VK_NULL_HANDLE, mySwapchain, mySurfaceFormat); assert(boolResult); std::vector<VkImage> mySwapchainImagesVector; std::vector<VkImageView> mySwapchainImageViewsVector; boolResult = vkdemos::getSwapchainImagesAndViews(myDevice, mySwapchain, mySurfaceFormat, mySwapchainImagesVector, mySwapchainImageViewsVector); assert(boolResult); VkCommandPool myCommandPool; boolResult = vkdemos::createCommandPool(myDevice, myQueueFamilyIndex, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, myCommandPool); assert(boolResult); VkCommandBuffer myCmdBufferInitialization; boolResult = vkdemos::allocateCommandBuffer(myDevice, myCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, myCmdBufferInitialization); assert(boolResult); /* * Initializations from Demo 02 (Triangle). */ VkPhysicalDeviceMemoryProperties myMemoryProperties; vkGetPhysicalDeviceMemoryProperties(myPhysicalDevice, &myMemoryProperties); // Create the Depth Buffer's Image and View. const VkFormat myDepthBufferFormat = VK_FORMAT_D16_UNORM; VkImage myDepthImage; VkImageView myDepthImageView; VkDeviceMemory myDepthMemory; boolResult = vkdemos::createAndAllocateImage(myDevice, myMemoryProperties, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0, myDepthBufferFormat, windowWidth, windowHeight, myDepthImage, myDepthMemory, &myDepthImageView, VK_IMAGE_ASPECT_DEPTH_BIT ); assert(boolResult); // Create the renderpass. VkRenderPass myRenderPass; boolResult = demo02CreateRenderPass(myDevice, mySurfaceFormat, myDepthBufferFormat, myRenderPass); assert(boolResult); // Create the Framebuffers, based on the number of swapchain images. std::vector<VkFramebuffer> myFramebuffersVector; myFramebuffersVector.reserve(mySwapchainImageViewsVector.size()); for(const auto view : mySwapchainImageViewsVector) { VkFramebuffer fb; boolResult = vkdemos::utils::createFramebuffer(myDevice, myRenderPass, {view, myDepthImageView}, windowWidth, windowHeight, fb); assert(boolResult); myFramebuffersVector.push_back(fb); } // Create a buffer to use as the vertex buffer. const size_t vertexBufferSize = sizeof(TriangleDemoVertex)*NUM_DEMO_VERTICES; VkBuffer myVertexBuffer; VkDeviceMemory myVertexBufferMemory; boolResult = vkdemos::createAndAllocateBuffer(myDevice, myMemoryProperties, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, vertexBufferSize, myVertexBuffer, myVertexBufferMemory ); assert(boolResult); // Map vertex buffer and insert data { void *mappedBuffer; result = vkMapMemory(myDevice, myVertexBufferMemory, 0, VK_WHOLE_SIZE, 0, &mappedBuffer); assert(result == VK_SUCCESS); memcpy(mappedBuffer, vertices, vertexBufferSize); vkUnmapMemory(myDevice, myVertexBufferMemory); } // Create the pipeline. const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, .setLayoutCount = 0, .pSetLayouts = nullptr, .pushConstantRangeCount = 0, .pPushConstantRanges = nullptr, }; VkPipelineLayout myPipelineLayout; result = vkCreatePipelineLayout(myDevice, &pipelineLayoutCreateInfo, nullptr, &myPipelineLayout); assert(result == VK_SUCCESS); // Create Pipeline. VkPipeline myGraphicsPipeline; boolResult = demo02CreatePipeline(myDevice, myRenderPass, myPipelineLayout, VERTEX_SHADER_FILENAME, FRAGMENT_SHADER_FILENAME, VERTEX_INPUT_BINDING, myGraphicsPipeline); assert(boolResult); /* * In the PerFrameData struct we group all the objects that are used in a single frame, and that * must remain valid for the duration of that frame. * */ PerFrameData perFrameDataVector[FRAME_LAG]; for(int i = 0; i < FRAME_LAG; i++) { boolResult = vkdemos::allocateCommandBuffer(myDevice, myCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, perFrameDataVector[i].presentCmdBuffer); assert(boolResult); result = vkdemos::utils::createFence(myDevice, perFrameDataVector[i].presentFence); assert(result == VK_SUCCESS); result = vkdemos::utils::createSemaphore(myDevice, perFrameDataVector[i].imageAcquiredSemaphore); assert(result == VK_SUCCESS); result = vkdemos::utils::createSemaphore(myDevice, perFrameDataVector[i].renderingCompletedSemaphore); assert(result == VK_SUCCESS); perFrameDataVector[i].fenceInitialized = false; } /* * Generation and submission of the initialization commands' command buffer. */ // We fill the initialization command buffer with... the initialization commands. boolResult = demo02FillInitializationCommandBuffer(myCmdBufferInitialization, myDepthImage); assert(boolResult); // We now submit the command buffer to the queue we created before, and we wait // for its completition. VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreCount = 0, .pWaitSemaphores = nullptr, .pWaitDstStageMask = nullptr, .commandBufferCount = 1, .pCommandBuffers = &myCmdBufferInitialization, .signalSemaphoreCount = 0, .pSignalSemaphores = nullptr }; result = vkQueueSubmit(myQueue, 1, &submitInfo, VK_NULL_HANDLE); assert(result == VK_SUCCESS); // Wait for the queue to complete its work. result = vkQueueWaitIdle(myQueue); assert(result == VK_SUCCESS); /* * Event loop */ SDL_Event sdlEvent; bool quit = false; // Just some variables for frame statistics long frameNumber = 0; long frameMaxTime = LONG_MIN; long frameMinTime = LONG_MAX; long frameAvgTimeSum = 0; long frameAvgTimeSumSquare = 0; constexpr long FRAMES_PER_STAT = 120; // How many frames to wait before printing frame time statistics. // The main event/render loop. while(!quit) { // Process events for this frame while(SDL_PollEvent(&sdlEvent)) { if (sdlEvent.type == SDL_QUIT) { quit = true; } if (sdlEvent.type == SDL_KEYDOWN && sdlEvent.key.keysym.sym == SDLK_ESCAPE) { quit = true; } } // Rendering code if(!quit) { // Render a single frame auto renderStartTime = std::chrono::high_resolution_clock::now(); quit = !demo03RenderSingleFrame(myDevice, myQueue, mySwapchain, myFramebuffersVector, myRenderPass, myGraphicsPipeline, myVertexBuffer, VERTEX_INPUT_BINDING, perFrameDataVector[frameNumber % FRAME_LAG], windowWidth, windowHeight); auto renderStopTime = std::chrono::high_resolution_clock::now(); // Compute frame time statistics auto elapsedTimeUs = std::chrono::duration_cast<std::chrono::microseconds>(renderStopTime - renderStartTime).count(); frameMaxTime = std::max(frameMaxTime, elapsedTimeUs); frameMinTime = std::min(frameMinTime, elapsedTimeUs); frameAvgTimeSum += elapsedTimeUs; frameAvgTimeSumSquare += elapsedTimeUs*elapsedTimeUs; // Print statistics if necessary if(frameNumber % FRAMES_PER_STAT == 0) { auto average = frameAvgTimeSum/FRAMES_PER_STAT; auto stddev = std::sqrt(frameAvgTimeSumSquare/FRAMES_PER_STAT - average*average); std::cout << "Frame time: average " << std::setw(6) << average << " us, maximum " << std::setw(6) << frameMaxTime << " us, minimum " << std::setw(6) << frameMinTime << " us, stddev " << (long)stddev << " (" << std::fixed << std::setprecision(2) << (stddev/average * 100.0f) << "%)" << std::endl; frameMaxTime = LONG_MIN; frameMinTime = LONG_MAX; frameAvgTimeSum = 0; frameAvgTimeSumSquare = 0; } frameNumber++; } } /* * Deinitialization */ // We wait for pending operations to complete before starting to destroy stuff. result = vkQueueWaitIdle(myQueue); assert(result == VK_SUCCESS); // Destroy the objects in the perFrameDataVector array. for(int i = 0; i < FRAME_LAG; i++) { vkDestroyFence(myDevice, perFrameDataVector[i].presentFence, nullptr); vkDestroySemaphore(myDevice, perFrameDataVector[i].imageAcquiredSemaphore, nullptr); vkDestroySemaphore(myDevice, perFrameDataVector[i].renderingCompletedSemaphore, nullptr); } /* * For more informations on the following commands, refer to Demo 02. */ vkDestroyPipeline(myDevice, myGraphicsPipeline, nullptr); vkDestroyPipelineLayout(myDevice, myPipelineLayout, nullptr); vkDestroyBuffer(myDevice, myVertexBuffer, nullptr); vkFreeMemory(myDevice, myVertexBufferMemory, nullptr); for(auto framebuffer : myFramebuffersVector) vkDestroyFramebuffer(myDevice, framebuffer, nullptr); vkDestroyRenderPass(myDevice, myRenderPass, nullptr); vkDestroyImageView(myDevice, myDepthImageView, nullptr); vkDestroyImage(myDevice, myDepthImage, nullptr); vkFreeMemory(myDevice, myDepthMemory, nullptr); /* * For more informations on the following commands, refer to Demo 01. */ vkDestroyCommandPool(myDevice, myCommandPool, nullptr); for(auto imgView : mySwapchainImageViewsVector) vkDestroyImageView(myDevice, imgView, nullptr); vkDestroySwapchainKHR(myDevice, mySwapchain, nullptr); vkDestroyDevice(myDevice, nullptr); vkDestroySurfaceKHR(myInstance, mySurface, nullptr); vkdemos::destroyDebugReportCallback(myInstance, myDebugReportCallback); vkDestroyInstance(myInstance, nullptr); SDL_DestroyWindow(mySdlWindow); SDL_Quit(); return 0; }
//============================================================================== // Vulkan破棄 //============================================================================== void destroyVulkan() { for(auto& frameBuffer : g_frameBuffers) { vkDestroyFramebuffer(g_VulkanDevice, frameBuffer, nullptr); } if(g_depthBufferTexture.view) { vkDestroyImageView(g_VulkanDevice, g_depthBufferTexture.view, nullptr); } if(g_depthBufferTexture.image) { vkDestroyImage(g_VulkanDevice, g_depthBufferTexture.image, nullptr); } if(g_depthBufferTexture.memory) { vkFreeMemory(g_VulkanDevice, g_depthBufferTexture.memory, nullptr); } if(g_commandBuffers.empty() == false) { vkFreeCommandBuffers(g_VulkanDevice, g_VulkanCommandPool, SWAP_CHAIN_COUNT, g_commandBuffers.data()); } if(g_VulkanCommandPool) { vkDestroyCommandPool(g_VulkanDevice, g_VulkanCommandPool, nullptr); } if(g_VulkanSemahoreRenderComplete) { vkDestroySemaphore(g_VulkanDevice, g_VulkanSemahoreRenderComplete, nullptr); } if(g_VulkanFence) { vkDestroyFence(g_VulkanDevice, g_VulkanFence, nullptr); } if(g_VulkanSwapChain) { vkDestroySwapchainKHR(g_VulkanDevice, g_VulkanSwapChain, nullptr); } if(g_VulkanSurface) { vkDestroySurfaceKHR(g_VulkanInstance, g_VulkanSurface, nullptr); } if(g_VulkanDevice) { vkDestroyDevice(g_VulkanDevice, nullptr); } if(g_VulkanInstance) { vkDestroyInstance(g_VulkanInstance, nullptr); } g_frameBuffers.clear(); g_commandBuffers.clear(); g_VulkanSurface = nullptr; g_VulkanSwapChain = nullptr; g_VulkanCommandPool = nullptr; g_VulkanSemahoreRenderComplete = nullptr; g_VulkanFence = nullptr; g_VulkanQueue = nullptr; g_VulkanDevice = nullptr; g_VulkanInstance = nullptr; }
void VulkanWindow::InitializeSwapchain() { vkGetPhysicalDeviceSurfaceCapabilitiesKHR ( mVulkanRenderer.GetPhysicalDevice(), mVkSurfaceKHR, &mVkSurfaceCapabilitiesKHR ); uint32_t surface_format_count = 0; vkGetPhysicalDeviceSurfaceFormatsKHR ( mVulkanRenderer.GetPhysicalDevice(), mVkSurfaceKHR, &surface_format_count, nullptr ); if ( surface_format_count == 0 ) { std::ostringstream stream; stream << "Physical device reports no surface formats."; throw std::runtime_error ( stream.str().c_str() ); } VkSurfaceFormatKHR surface_format_khr; std::vector<VkSurfaceFormatKHR> surface_format_list ( surface_format_count ); vkGetPhysicalDeviceSurfaceFormatsKHR ( mVulkanRenderer.GetPhysicalDevice(), mVkSurfaceKHR, &surface_format_count, surface_format_list.data() ); if ( surface_format_list[0].format == VK_FORMAT_UNDEFINED ) { surface_format_khr = mVulkanRenderer.GetSurfaceFormatKHR(); } else { surface_format_khr = surface_format_list[0]; } if ( mSwapchainImageCount < mVkSurfaceCapabilitiesKHR.minImageCount ) { mSwapchainImageCount = mVkSurfaceCapabilitiesKHR.minImageCount; } if ( ( mVkSurfaceCapabilitiesKHR.maxImageCount > 0 ) && ( mSwapchainImageCount > mVkSurfaceCapabilitiesKHR.maxImageCount ) ) { mSwapchainImageCount = mVkSurfaceCapabilitiesKHR.maxImageCount; } VkSwapchainCreateInfoKHR swapchain_create_info{}; swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchain_create_info.surface = mVkSurfaceKHR; swapchain_create_info.minImageCount = mSwapchainImageCount; swapchain_create_info.imageFormat = surface_format_khr.format; swapchain_create_info.imageColorSpace = surface_format_khr.colorSpace; swapchain_create_info.imageExtent.width = mVkSurfaceCapabilitiesKHR.currentExtent.width; swapchain_create_info.imageExtent.height = mVkSurfaceCapabilitiesKHR.currentExtent.height; 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 = 0; swapchain_create_info.pQueueFamilyIndices = nullptr; swapchain_create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; // This may be reset below. swapchain_create_info.clipped = VK_TRUE; swapchain_create_info.oldSwapchain = mVkSwapchainKHR; // Used for Resising. { uint32_t present_mode_count = 0; vkGetPhysicalDeviceSurfacePresentModesKHR ( mVulkanRenderer.GetPhysicalDevice(), mVkSurfaceKHR, &present_mode_count, nullptr ); std::vector<VkPresentModeKHR> present_mode_list ( present_mode_count ); vkGetPhysicalDeviceSurfacePresentModesKHR ( mVulkanRenderer.GetPhysicalDevice(), mVkSurfaceKHR, &present_mode_count, present_mode_list.data() ); for ( auto& i : present_mode_list ) { if ( i == VK_PRESENT_MODE_MAILBOX_KHR ) { swapchain_create_info.presentMode = i; break; } } } if ( VkResult result = vkCreateSwapchainKHR ( mVulkanRenderer.GetDevice(), &swapchain_create_info, nullptr, &mVkSwapchainKHR ) ) { std::ostringstream stream; stream << "Call to vkCreateSwapchainKHR failed: ( " << GetVulkanResultString ( result ) << " )"; throw std::runtime_error ( stream.str().c_str() ); } if ( swapchain_create_info.oldSwapchain != VK_NULL_HANDLE ) { vkDestroySwapchainKHR ( mVulkanRenderer.GetDevice(), swapchain_create_info.oldSwapchain, nullptr ); } }
//[-------------------------------------------------------] //[ Public methods ] //[-------------------------------------------------------] SwapChain::SwapChain(VulkanRenderer &vulkanRenderer, handle nativeWindowHandle) : ISwapChain(vulkanRenderer), mNativeWindowHandle(nativeWindowHandle), mVkSurfaceKHR(VK_NULL_HANDLE), mVkSwapchainKHR(VK_NULL_HANDLE), mSwapchainImageCount(0) { // Get the Vulkan instance and the Vulkan physical device const VkInstance vkInstance = vulkanRenderer.getVulkanRuntimeLinking().getVkInstance(); const IContext& context = vulkanRenderer.getContext(); const VkPhysicalDevice vkPhysicalDevice = context.getVkPhysicalDevice(); const VkDevice vkDevice = context.getVkDevice(); // Create Vulkan surface instance depending on OS #ifdef _WIN32 VkWin32SurfaceCreateInfoKHR vkWin32SurfaceCreateInfoKHR = {}; vkWin32SurfaceCreateInfoKHR.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; vkWin32SurfaceCreateInfoKHR.hinstance = reinterpret_cast<HINSTANCE>(::GetWindowLong(reinterpret_cast<HWND>(nativeWindowHandle), GWL_HINSTANCE)); vkWin32SurfaceCreateInfoKHR.hwnd = reinterpret_cast<HWND>(nativeWindowHandle); VkResult vkResult = vkCreateWin32SurfaceKHR(vkInstance, &vkWin32SurfaceCreateInfoKHR, nullptr, &mVkSurfaceKHR); #else #ifdef __ANDROID__ // TODO(co) Not tested - see https://github.com/SaschaWillems/Vulkan VkAndroidSurfaceCreateInfoKHR vkAndroidSurfaceCreateInfoKHR = {}; vkAndroidSurfaceCreateInfoKHR.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; vkAndroidSurfaceCreateInfoKHR.window = window; VkResult vkResult = vkCreateAndroidSurfaceKHR(vkInstance, &vkAndroidSurfaceCreateInfoKHR, nullptr, &mVkSurfaceKHR); #else // TODO(co) Not tested - see https://github.com/SaschaWillems/Vulkan VkXcbSurfaceCreateInfoKHR vkXcbSurfaceCreateInfoKHR = {}; vkXcbSurfaceCreateInfoKHR.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; vkXcbSurfaceCreateInfoKHR.connection = connection; vkXcbSurfaceCreateInfoKHR.window = window; VkResult vkResult = vkCreateXcbSurfaceKHR(vkInstance, &vkXcbSurfaceCreateInfoKHR, nullptr, &mVkSurfaceKHR); #endif #endif // Get list of supported surface formats uint32_t surfaceFormatCount = 0; vkResult = vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, mVkSurfaceKHR, &surfaceFormatCount, nullptr); // assert(!vkResult); // assert(surfaceFormatCount > 0); std::vector<VkSurfaceFormatKHR> vkSurfaceFormatKHRs(surfaceFormatCount); vkResult = vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, mVkSurfaceKHR, &surfaceFormatCount, vkSurfaceFormatKHRs.data()); // assert(!vkResult); // If the surface format list only includes one entry with VK_FORMAT_UNDEFINED, // there is no preferred format, so we assume VK_FORMAT_B8G8R8A8_UNORM VkFormat colorVkFormat; if ((surfaceFormatCount == 1) && (vkSurfaceFormatKHRs[0].format == VK_FORMAT_UNDEFINED)) { colorVkFormat = VK_FORMAT_B8G8R8A8_UNORM; } else { // Always select the first available color format // If you need a specific format (e.g. SRGB) you'd need to // iterate over the list of available surface format and // check for it's presence colorVkFormat = vkSurfaceFormatKHRs[0].format; } VkColorSpaceKHR vkColorSpaceKHR = vkSurfaceFormatKHRs[0].colorSpace; // Get the width and height of the given native window and ensure they are never ever zero // -> See "getSafeWidthAndHeight()"-method comments for details uint32_t width = 1; uint32_t height = 1; #ifdef _WIN32 { // Get the client rectangle of the given native window RECT rect; ::GetClientRect(reinterpret_cast<HWND>(nativeWindowHandle), &rect); // Get the width and height... width = static_cast<uint32_t>(rect.right - rect.left); height = static_cast<uint32_t>(rect.bottom - rect.top); // ... and ensure that none of them is ever zero if (width < 1) { width = 1; } if (height < 1) { height = 1; } } #endif // TODO(co) Move the rest into a method VkSwapchainKHR oldVkSwapchainKHR = mVkSwapchainKHR; // Get physical device surface properties and formats VkSurfaceCapabilitiesKHR vkSurfaceCapabilitiesKHR; vkResult = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkPhysicalDevice, mVkSurfaceKHR, &vkSurfaceCapabilitiesKHR); // assert(!vkResult); // Get available present modes uint32_t presentModeCount = 0; vkResult = vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, mVkSurfaceKHR, &presentModeCount, nullptr); // assert(!vkResult); // assert(presentModeCount > 0); std::vector<VkPresentModeKHR> vkPresentModeKHRs(presentModeCount); vkResult = vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, mVkSurfaceKHR, &presentModeCount, vkPresentModeKHRs.data()); // assert(!vkResult); // Width and height are either both -1, or both not -1. VkExtent2D swapchainExtent = {}; if (vkSurfaceCapabilitiesKHR.currentExtent.width == -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 = vkSurfaceCapabilitiesKHR.currentExtent; width = vkSurfaceCapabilitiesKHR.currentExtent.width; height = vkSurfaceCapabilitiesKHR.currentExtent.height; } // Prefer mailbox mode if present, it's the lowest latency non-tearing present mode VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; for (size_t i = 0; i < presentModeCount; ++i) { if (vkPresentModeKHRs[i] == VK_PRESENT_MODE_MAILBOX_KHR) { swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; break; } if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (vkPresentModeKHRs[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; } } // Determine the number of images uint32_t desiredNumberOfSwapchainImages = vkSurfaceCapabilitiesKHR.minImageCount + 1; if ((vkSurfaceCapabilitiesKHR.maxImageCount > 0) && (desiredNumberOfSwapchainImages > vkSurfaceCapabilitiesKHR.maxImageCount)) { desiredNumberOfSwapchainImages = vkSurfaceCapabilitiesKHR.maxImageCount; } VkSurfaceTransformFlagsKHR vkSurfaceTransformFlagsKHR; if (vkSurfaceCapabilitiesKHR.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { vkSurfaceTransformFlagsKHR = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } else { vkSurfaceTransformFlagsKHR = vkSurfaceCapabilitiesKHR.currentTransform; } VkSwapchainCreateInfoKHR vkSwapchainCreateInfoKHR = {}; vkSwapchainCreateInfoKHR.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; vkSwapchainCreateInfoKHR.surface = mVkSurfaceKHR; vkSwapchainCreateInfoKHR.minImageCount = desiredNumberOfSwapchainImages; vkSwapchainCreateInfoKHR.imageFormat = colorVkFormat; vkSwapchainCreateInfoKHR.imageColorSpace = vkColorSpaceKHR; vkSwapchainCreateInfoKHR.imageExtent = { swapchainExtent.width, swapchainExtent.height }; vkSwapchainCreateInfoKHR.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; vkSwapchainCreateInfoKHR.preTransform = static_cast<VkSurfaceTransformFlagBitsKHR>(vkSurfaceTransformFlagsKHR); vkSwapchainCreateInfoKHR.imageArrayLayers = 1; vkSwapchainCreateInfoKHR.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; vkSwapchainCreateInfoKHR.presentMode = swapchainPresentMode; vkSwapchainCreateInfoKHR.oldSwapchain = oldVkSwapchainKHR; vkSwapchainCreateInfoKHR.clipped = true; vkSwapchainCreateInfoKHR.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; vkResult = vkCreateSwapchainKHR(vkDevice, &vkSwapchainCreateInfoKHR, nullptr, &mVkSwapchainKHR); // assert(!vkResult); // If an existing swap chain is re-created, destroy the old swap chain // This also cleans up all the presentable images if (VK_NULL_HANDLE != oldVkSwapchainKHR) { for (uint32_t i = 0; i < mSwapchainImageCount; ++i) { vkDestroyImageView(vkDevice, mSwapChainBuffer[i].view, nullptr); } vkDestroySwapchainKHR(vkDevice, oldVkSwapchainKHR, nullptr); } vkResult = vkGetSwapchainImagesKHR(vkDevice, mVkSwapchainKHR, &mSwapchainImageCount, nullptr); // assert(!vkResult); // Get the swap chain images mVkImages.resize(mSwapchainImageCount); vkResult = vkGetSwapchainImagesKHR(vkDevice, mVkSwapchainKHR, &mSwapchainImageCount, mVkImages.data()); // assert(!vkResult); // Get the swap chain buffers containing the image and image view mSwapChainBuffer.resize(mSwapchainImageCount); for (uint32_t i = 0; i < mSwapchainImageCount; ++i) { VkImageViewCreateInfo colorAttachmentView = {}; colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; colorAttachmentView.format = colorVkFormat; 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.levelCount = 1; colorAttachmentView.subresourceRange.layerCount = 1; colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; mSwapChainBuffer[i].image = mVkImages[i]; // Transform images from initial (undefined) to present layout Helper::setImageLayout(context.getSetupVkCommandBuffer(), mSwapChainBuffer[i].image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_ASPECT_COLOR_BIT); colorAttachmentView.image = mSwapChainBuffer[i].image; vkResult = vkCreateImageView(vkDevice, &colorAttachmentView, nullptr, &mSwapChainBuffer[i].view); // assert(!vkResult); } }
int main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; struct sample_info info = {}; char sample_title[] = "Swapchain Initialization Sample"; /* * Set up swapchain: * - Get supported uses for all queues * - Try to find a queue that supports both graphics and present * - If no queue supports both, find a present queue and make sure we have a * graphics queue * - Get a list of supported formats and use the first one * - Get surface properties and present modes and use them to create a swap * chain * - Create swap chain buffers * - For each buffer, create a color attachment view and set its layout to * color attachment */ init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); init_connection(info); init_window_size(info, 50, 50); init_window(info); /* VULKAN_KEY_START */ // Construct the surface description: #ifdef _WIN32 VkWin32SurfaceCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; createInfo.pNext = NULL; createInfo.hinstance = info.connection; createInfo.hwnd = info.window; res = vkCreateWin32SurfaceKHR(info.inst, &createInfo, NULL, &info.surface); #else // _WIN32 VkXcbSurfaceCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; createInfo.pNext = NULL; createInfo.connection = info.connection; createInfo.window = info.window; res = vkCreateXcbSurfaceKHR(info.inst, &createInfo, NULL, &info.surface); #endif // _WIN32 assert(res == VK_SUCCESS); // Iterate over each queue to learn whether it supports presenting: VkBool32 *supportsPresent = (VkBool32 *)malloc(info.queue_count * sizeof(VkBool32)); for (uint32_t i = 0; i < info.queue_count; i++) { vkGetPhysicalDeviceSurfaceSupportKHR(info.gpus[0], i, info.surface, &supportsPresent[i]); } // Search for a graphics queue and a present queue in the array of queue // families, try to find one that supports both uint32_t graphicsQueueNodeIndex = UINT32_MAX; for (uint32_t i = 0; i < info.queue_count; i++) { if ((info.queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { if (supportsPresent[i] == VK_TRUE) { graphicsQueueNodeIndex = i; break; } } } free(supportsPresent); // Generate error if could not find a queue that supports both a graphics // and present if (graphicsQueueNodeIndex == UINT32_MAX) { std::cout << "Could not find a queue that supports both graphics and " "present\n"; exit(-1); } info.graphics_queue_family_index = graphicsQueueNodeIndex; init_device(info); // Get the list of VkFormats that are supported: uint32_t formatCount; res = vkGetPhysicalDeviceSurfaceFormatsKHR(info.gpus[0], info.surface, &formatCount, NULL); assert(res == VK_SUCCESS); VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); res = vkGetPhysicalDeviceSurfaceFormatsKHR(info.gpus[0], info.surface, &formatCount, surfFormats); assert(res == VK_SUCCESS); // If the format list includes just one entry of VK_FORMAT_UNDEFINED, // the surface has no preferred format. Otherwise, at least one // supported format will be returned. if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { info.format = VK_FORMAT_B8G8R8A8_UNORM; } else { assert(formatCount >= 1); info.format = surfFormats[0].format; } VkSurfaceCapabilitiesKHR surfCapabilities; res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(info.gpus[0], info.surface, &surfCapabilities); assert(res == VK_SUCCESS); uint32_t presentModeCount; res = vkGetPhysicalDeviceSurfacePresentModesKHR(info.gpus[0], info.surface, &presentModeCount, NULL); assert(res == VK_SUCCESS); VkPresentModeKHR *presentModes = (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR)); res = vkGetPhysicalDeviceSurfacePresentModesKHR( info.gpus[0], info.surface, &presentModeCount, presentModes); assert(res == VK_SUCCESS); VkExtent2D swapChainExtent; // width and height are either both -1, or both not -1. if (surfCapabilities.currentExtent.width == (uint32_t)-1) { // If the surface size is undefined, the size is set to // the size of the images requested. swapChainExtent.width = info.width; swapChainExtent.height = info.height; } else { // If the surface size is defined, the swap chain size must match swapChainExtent = surfCapabilities.currentExtent; } // If mailbox mode is available, use it, as is the lowest-latency non- // tearing mode. If not, try IMMEDIATE which will usually be available, // and is fastest (though it tears). If not, fall back to FIFO which is // always available. VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; for (size_t i = 0; i < presentModeCount; i++) { if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; break; } if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; } } // Determine the number of VkImage's to use in the swap chain (we desire to // own only 1 image at a time, besides the images being displayed and // queued for display): uint32_t desiredNumberOfSwapChainImages = surfCapabilities.minImageCount + 1; if ((surfCapabilities.maxImageCount > 0) && (desiredNumberOfSwapChainImages > surfCapabilities.maxImageCount)) { // Application must settle for fewer images than desired: desiredNumberOfSwapChainImages = surfCapabilities.maxImageCount; } VkSurfaceTransformFlagBitsKHR preTransform; if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } else { preTransform = surfCapabilities.currentTransform; } VkSwapchainCreateInfoKHR swap_chain = {}; swap_chain.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swap_chain.pNext = NULL; swap_chain.surface = info.surface; swap_chain.minImageCount = desiredNumberOfSwapChainImages; swap_chain.imageFormat = info.format; swap_chain.imageExtent.width = swapChainExtent.width; swap_chain.imageExtent.height = swapChainExtent.height; swap_chain.preTransform = preTransform; swap_chain.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swap_chain.imageArrayLayers = 1; swap_chain.presentMode = swapchainPresentMode; swap_chain.oldSwapchain = NULL; swap_chain.clipped = true; swap_chain.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; swap_chain.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swap_chain.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swap_chain.queueFamilyIndexCount = 0; swap_chain.pQueueFamilyIndices = NULL; res = vkCreateSwapchainKHR(info.device, &swap_chain, NULL, &info.swap_chain); assert(res == VK_SUCCESS); res = vkGetSwapchainImagesKHR(info.device, info.swap_chain, &info.swapchainImageCount, NULL); assert(res == VK_SUCCESS); VkImage *swapchainImages = (VkImage *)malloc(info.swapchainImageCount * sizeof(VkImage)); assert(swapchainImages); res = vkGetSwapchainImagesKHR(info.device, info.swap_chain, &info.swapchainImageCount, swapchainImages); assert(res == VK_SUCCESS); info.buffers.resize(info.swapchainImageCount); // Going to need a command buffer to send the memory barriers in // set_image_layout but we couldn't have created one before we knew // what our graphics_queue_family_index is, but now that we have it, // create the command buffer init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); vkGetDeviceQueue(info.device, info.graphics_queue_family_index, 0, &info.queue); for (uint32_t i = 0; i < info.swapchainImageCount; i++) { VkImageViewCreateInfo color_image_view = {}; color_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; color_image_view.pNext = NULL; color_image_view.format = info.format; color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; color_image_view.subresourceRange.baseMipLevel = 0; color_image_view.subresourceRange.levelCount = 1; color_image_view.subresourceRange.baseArrayLayer = 0; color_image_view.subresourceRange.layerCount = 1; color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; color_image_view.flags = 0; info.buffers[i].image = swapchainImages[i]; set_image_layout(info, info.buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); color_image_view.image = info.buffers[i].image; res = vkCreateImageView(info.device, &color_image_view, NULL, &info.buffers[i].view); assert(res == VK_SUCCESS); } free(swapchainImages); execute_end_command_buffer(info); execute_queue_command_buffer(info); /* VULKAN_KEY_END */ /* Clean Up */ VkCommandBuffer cmd_bufs[1] = {info.cmd}; vkFreeCommandBuffers(info.device, info.cmd_pool, 1, cmd_bufs); vkDestroyCommandPool(info.device, info.cmd_pool, NULL); for (uint32_t i = 0; i < info.swapchainImageCount; i++) { vkDestroyImageView(info.device, info.buffers[i].view, NULL); } vkDestroySwapchainKHR(info.device, info.swap_chain, NULL); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
GrManagerImpl::~GrManagerImpl() { // FIRST THING: wait for the GPU if(m_queue) { LockGuard<Mutex> lock(m_globalMtx); vkQueueWaitIdle(m_queue); m_queue = VK_NULL_HANDLE; } // SECOND THING: The destroy everything that has a reference to GrObjects. for(auto& x : m_backbuffers) { if(x.m_imageView) { vkDestroyImageView(m_device, x.m_imageView, nullptr); x.m_imageView = VK_NULL_HANDLE; } } for(auto& x : m_perFrame) { x.m_presentFence.reset(nullptr); x.m_acquireSemaphore.reset(nullptr); x.m_renderSemaphore.reset(nullptr); x.m_cmdbsSubmitted.destroy(getAllocator()); } m_perThread.destroy(getAllocator()); if(m_samplerCache) { getAllocator().deleteInstance(m_samplerCache); } // THIRD THING: Continue with the rest m_rpCreator.destroy(); m_pplineLayFactory.destroy(); m_dsetAlloc.destroy(); m_transientMem.destroy(); m_gpuMemManager.destroy(); m_semaphores.destroy(); // Destroy before fences m_fences.destroy(); if(m_swapchain) { vkDestroySwapchainKHR(m_device, m_swapchain, nullptr); } if(m_pplineCache) { vkDestroyPipelineCache(m_device, m_pplineCache, nullptr); } if(m_device) { vkDestroyDevice(m_device, nullptr); } if(m_surface) { vkDestroySurfaceKHR(m_instance, m_surface, nullptr); } if(m_instance) { vkDestroyInstance(m_instance, nullptr); } }