/** * Create the swapchain and get it's Images with given width and height * * @param width Pointer to the width of the swapchain (may be adjusted to fit the requirements of the swapchain) * @param height Pointer to the height of the swapchain (may be adjusted to fit the requirements of the swapchain) * @param vsync (Optional) Can be used to force vsync'd rendering (by using VK_PRESENT_MODE_FIFO_KHR as presentation mode) */ public unsafe void Create(ref uint width, ref uint height, bool vsync = false) { VkResult err; VkSwapchainKHR oldSwapchain = swapchain; // Get physical Device Surface properties and formats VkSurfaceCapabilitiesKHR surfCaps; err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(Device.PhysicalDevice, Surface, out surfCaps); Debug.Assert(err == VkResult.Success); // Get available present modes uint presentModeCount; err = vkGetPhysicalDeviceSurfacePresentModesKHR(Device.PhysicalDevice, Surface, &presentModeCount, null); Debug.Assert(err == VkResult.Success); Debug.Assert(presentModeCount > 0); using (Vector <VkPresentModeKHR> presentModes = new Vector <VkPresentModeKHR>(presentModeCount)) { err = vkGetPhysicalDeviceSurfacePresentModesKHR(Device.PhysicalDevice, Surface, &presentModeCount, (VkPresentModeKHR *)presentModes.Data); Debug.Assert(err == VkResult.Success); presentModes.Count = presentModeCount; // If width (and height) equals the special value 0xFFFFFFFF, the size of the Surface will be set by the swapchain if (surfCaps.currentExtent.width != unchecked ((uint)-1)) { width = surfCaps.currentExtent.width; height = surfCaps.currentExtent.height; } extent = new VkExtent3D(width, height, 1); // Select a present mode for the swapchain // 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 = VkPresentModeKHR.Fifo; // If v-sync is not requested, try to find a mailbox mode // It's the lowest latency non-tearing present mode available if (!vsync) { for (uint i = 0; i < presentModeCount; i++) { if (presentModes[i] == VkPresentModeKHR.Mailbox) { swapchainPresentMode = VkPresentModeKHR.Mailbox; break; } if ((swapchainPresentMode != VkPresentModeKHR.Mailbox) && (presentModes[i] == VkPresentModeKHR.Immediate)) { swapchainPresentMode = VkPresentModeKHR.Immediate; } } } // Determine the number of Images uint desiredNumberOfSwapchainImages = IMAGE_COUNT;// surfCaps.minImageCount + 1; if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount)) { Debug.Assert(false); desiredNumberOfSwapchainImages = surfCaps.maxImageCount; } // Find the transformation of the Surface VkSurfaceTransformFlagsKHR preTransform; if ((surfCaps.supportedTransforms & VkSurfaceTransformFlagsKHR.Identity) != 0) { // We prefer a non-rotated transform preTransform = VkSurfaceTransformFlagsKHR.Identity; } else { preTransform = surfCaps.currentTransform; } VkSwapchainCreateInfoKHR swapchainCI = new VkSwapchainCreateInfoKHR { sType = VkStructureType.SwapchainCreateInfoKHR, pNext = null, surface = Surface, minImageCount = desiredNumberOfSwapchainImages, imageFormat = ColorFormat, imageColorSpace = ColorSpace, imageExtent = new VkExtent2D(extent.width, extent.height), imageUsage = VkImageUsageFlags.ColorAttachment, preTransform = preTransform, imageArrayLayers = 1, imageSharingMode = VkSharingMode.Exclusive, queueFamilyIndexCount = 0, pQueueFamilyIndices = null, presentMode = swapchainPresentMode, oldSwapchain = oldSwapchain, // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the Surface area clipped = true, compositeAlpha = VkCompositeAlphaFlagsKHR.Opaque }; // Set additional usage flag for blitting from the swapchain Images if supported Device.GetPhysicalDeviceFormatProperties(ColorFormat, out VkFormatProperties formatProps); if ((formatProps.optimalTilingFeatures & VkFormatFeatureFlags.BlitDst) != 0) { swapchainCI.imageUsage |= VkImageUsageFlags.TransferSrc; } swapchain = Device.CreateSwapchainKHR(ref swapchainCI); // If an existing swap chain is re-created, destroy the old swap chain // This also cleans up all the presentable Images if (oldSwapchain.Handle != 0) { for (uint i = 0; i < ImageCount; i++) { ImageViews[i].Dispose(); } Device.DestroySwapchainKHR(oldSwapchain); } var vkImages = Vulkan.vkGetSwapchainImagesKHR(Device.Handle, swapchain); VkImages.Clear(); Images = new Image[vkImages.Length]; ImageViews = new ImageView[vkImages.Length]; for (int i = 0; i < vkImages.Length; i++) { Images[i] = new Image(vkImages[i]) { imageType = VkImageType.Image2D, extent = extent }; ImageViews[i] = ImageView.Create(Images[i], VkImageViewType.Image2D, ColorFormat, VkImageAspectFlags.Color, 0, 1); } // Get the swap chain Images VkImages.Add(vkImages); } }