private void rebuildSwapchain() { var sCaps = VkKhr.PhysicalDeviceExtensions.GetSurfaceCapabilitiesKhr(_vkPhysicalDevice, Surface); // Calculate the size of the images if (sCaps.CurrentExtent.Width != Int32.MaxValue) // We have to use the given size { Extent = sCaps.CurrentExtent; } else // We can choose an extent, but we will just make it the size of the window { Extent = Point.Max(sCaps.MinImageExtent, Point.Min(sCaps.MaxImageExtent, _window.Size)); } // Calculate the number of images int imCount = sCaps.MinImageCount + 1; if (sCaps.MaxImageCount != 0) { imCount = Math.Min(imCount, sCaps.MaxImageCount); } _syncObjects.MaxInflightFrames = (uint)Math.Min(imCount, MAX_INFLIGHT_FRAMES); // Create the swapchain var oldSwapChain = _swapChain; VkKhr.SwapchainCreateInfoKhr cInfo = new VkKhr.SwapchainCreateInfoKhr( Surface, _surfaceFormat.Format, Extent, minImageCount: imCount, imageColorSpace: _surfaceFormat.ColorSpace, imageUsage: Vk.ImageUsages.ColorAttachment | Vk.ImageUsages.TransferDst, presentMode: _presentMode, oldSwapchain: oldSwapChain ); _swapChain = VkKhr.DeviceExtensions.CreateSwapchainKhr(_vkDevice, cInfo); // Destroy the old swapchain oldSwapChain?.Dispose(); // Get the new swapchain images var imgs = _swapChain.GetImages(); _swapChainImages = new SwapchainImage[imgs.Length]; imgs.ForEach((img, idx) => { Vk.ImageViewCreateInfo vInfo = new Vk.ImageViewCreateInfo( _surfaceFormat.Format, DEFAULT_SUBRESOURCE_RANGE, viewType: Vk.ImageViewType.Image2D, components: default ); var view = img.CreateView(vInfo); _swapChainImages[idx] = new SwapchainImage { Image = img, View = view, TransferBarrier = new Vk.ImageMemoryBarrier( img, new Vk.ImageSubresourceRange(Vk.ImageAspects.Color, 0, 1, 0, 1), Vk.Accesses.ColorAttachmentRead, Vk.Accesses.TransferWrite, Vk.ImageLayout.PresentSrcKhr, Vk.ImageLayout.TransferDstOptimal ), PresentBarrier = new Vk.ImageMemoryBarrier( img, new Vk.ImageSubresourceRange(Vk.ImageAspects.Color, 0, 1, 0, 1), Vk.Accesses.TransferWrite, Vk.Accesses.ColorAttachmentRead, Vk.ImageLayout.TransferDstOptimal, Vk.ImageLayout.PresentSrcKhr ) }; }); // Perform the initial layout transitions to present mode _commandBuffer.Begin(ONE_TIME_SUBMIT); var imb = new Vk.ImageMemoryBarrier(null, new Vk.ImageSubresourceRange(Vk.ImageAspects.Color, 0, 1, 0, 1), Vk.Accesses.TransferWrite, Vk.Accesses.ColorAttachmentRead, Vk.ImageLayout.Undefined, Vk.ImageLayout.PresentSrcKhr); _commandBuffer.CmdPipelineBarrier(Vk.PipelineStages.AllCommands, Vk.PipelineStages.AllCommands, imageMemoryBarriers: _swapChainImages.Select(sci => { imb.Image = sci.Image; return(imb); }).ToArray()); _commandBuffer.End(); _presentQueue.Submit(new Vk.SubmitInfo(commandBuffers: new[] { _commandBuffer }), _blitFence); _blitFence.Wait(); // Do not reset // Report LDEBUG($"Presentation swapchain rebuilt @ {Extent} " + $"(F:{_surfaceFormat.Format}:{_surfaceFormat.ColorSpace==VkKhr.ColorSpaceKhr.SRgbNonlinear} I:{_swapChainImages.Length}:{_syncObjects.MaxInflightFrames})."); Dirty = false; }
public Swapchain(GraphicsDevice gdevice, Vk.Instance instance, Vk.PhysicalDevice pDevice, Vk.Device device) { Device = gdevice; _window = gdevice.Application.Window; _vkInstance = instance; _vkPhysicalDevice = pDevice; _vkDevice = device; // Create the surface long surfHandle = Glfw.CreateWindowSurface(instance, _window.Handle); Vk.AllocationCallbacks?acb = null; Surface = new VkKhr.SurfaceKhr(instance, ref acb, surfHandle); if (VkKhr.PhysicalDeviceExtensions.GetSurfaceSupportKhr(pDevice, gdevice.Queues.Graphics.FamilyIndex, Surface) == Vk.Constant.False) { LFATAL($"The physical device '{gdevice.Info.Name}' does not support surface presentation."); throw new PlatformNotSupportedException("Physical device does not support surface presentation."); } LINFO("Created Vulkan presentation surface."); // Check the surface for swapchain support levels var sFmts = VkKhr.PhysicalDeviceExtensions.GetSurfaceFormatsKhr(pDevice, Surface); var pModes = VkKhr.PhysicalDeviceExtensions.GetSurfacePresentModesKhr(pDevice, Surface); if (sFmts.Length == 0) { throw new PlatformNotSupportedException("The chosen device does not support any presentation formats."); } if (pModes.Length == 0) { throw new PlatformNotSupportedException("The chosen device does not support any presentation modes."); } // Choose the best available surface format if (sFmts.Length == 1 && sFmts[0].Format == Vk.Format.Undefined) // We are allowed to pick! { _surfaceFormat = PREFERRED_SURFACE_FORMATS[0]; } else // Check if one of the preferred formats is available, otherwise just use the first format given { var sfmt = PREFERRED_SURFACE_FORMATS.FirstOrDefault(fmt => sFmts.Contains(fmt)); if (sfmt.Format == 0 && sfmt.ColorSpace == 0) { _surfaceFormat = sFmts[0]; } else { _surfaceFormat = sfmt; } } // Choose the presentation mode (prefer mailbox -> fifo -> imm) _presentMode = pModes.Contains(VkKhr.PresentModeKhr.Mailbox) ? VkKhr.PresentModeKhr.Mailbox : pModes.Contains(VkKhr.PresentModeKhr.Fifo) ? VkKhr.PresentModeKhr.Fifo : VkKhr.PresentModeKhr.Immediate; // Prepare the synchronization objects _syncObjects.ImageAvailable = new Vk.Semaphore[MAX_INFLIGHT_FRAMES]; _syncObjects.BlitComplete = new Vk.Semaphore[MAX_INFLIGHT_FRAMES]; for (int i = 0; i < MAX_INFLIGHT_FRAMES; ++i) { _syncObjects.ImageAvailable[i] = device.CreateSemaphore(); _syncObjects.BlitComplete[i] = device.CreateSemaphore(); } // Setup the command buffers var cpci = new Vk.CommandPoolCreateInfo(_presentQueue.FamilyIndex, Vk.CommandPoolCreateFlags.Transient | Vk.CommandPoolCreateFlags.ResetCommandBuffer); _commandPool = device.CreateCommandPool(cpci); var cbai = new Vk.CommandBufferAllocateInfo(Vk.CommandBufferLevel.Primary, 1); _commandBuffer = _commandPool.AllocateBuffers(cbai)[0]; _blitFence = device.CreateFence(); // Do NOT start this signalled, as it is needed in rebuildSwapchain() below _rtTransferBarrier = new Vk.ImageMemoryBarrier( null, new Vk.ImageSubresourceRange(Vk.ImageAspects.Color, 0, 1, 0, 1), Vk.Accesses.ColorAttachmentWrite, Vk.Accesses.TransferRead, Vk.ImageLayout.ColorAttachmentOptimal, Vk.ImageLayout.TransferSrcOptimal ); _rtAttachBarrier = new Vk.ImageMemoryBarrier( null, new Vk.ImageSubresourceRange(Vk.ImageAspects.Color, 0, 1, 0, 1), Vk.Accesses.TransferRead, Vk.Accesses.ColorAttachmentWrite, Vk.ImageLayout.TransferSrcOptimal, Vk.ImageLayout.ColorAttachmentOptimal ); // Build the swapchain rebuildSwapchain(); }
/// <summary> /// Rebuilds the render target to use a new size. /// </summary> /// <param name="width">The new width of the render target.</param> /// <param name="height">The new height of the render target.</param> /// <param name="force">If <c>true</c>, the render target will be rebuilt even if the size doesnt change.</param> public void Rebuild(uint width, uint height, bool force = false) { if (!force && width == Width && height == Height) { return; } Width = width; Height = height; // Destroy the existing objects, if needed VkView?.Dispose(); VkImage?.Dispose(); VkMemory?.Dispose(); // Create the image var ici = new Vk.ImageCreateInfo { ImageType = Vk.ImageType.Image2D, Extent = new Vk.Extent3D((int)width, (int)height, 1), MipLevels = 1, ArrayLayers = 1, Format = (Vk.Format)Format, Tiling = Vk.ImageTiling.Optimal, InitialLayout = Vk.ImageLayout.Undefined, Usage = Vk.ImageUsages.Sampled | Vk.ImageUsages.TransferSrc | Vk.ImageUsages.TransferDst | (HasDepth ? Vk.ImageUsages.DepthStencilAttachment : Vk.ImageUsages.ColorAttachment), // No subpassInput support yet, but add it here SharingMode = Vk.SharingMode.Exclusive, Samples = Vk.SampleCounts.Count1, // TODO: Change when we support multisampling Flags = Vk.ImageCreateFlags.None }; VkImage = Device.VkDevice.CreateImage(ici); // Create the backing memory for the image var memReq = VkImage.GetMemoryRequirements(); var memIdx = Device.FindMemoryTypeIndex(memReq.MemoryTypeBits, Vk.MemoryProperties.DeviceLocal); if (memIdx == -1) { throw new InvalidOperationException("Cannot find a memory type that supports render targets (this means bad or out-of-date hardware)"); } var mai = new Vk.MemoryAllocateInfo(memReq.Size, memIdx); VkMemory = Device.VkDevice.AllocateMemory(mai); DataSize = (uint)memReq.Size; VkImage.BindMemory(VkMemory); // Create the view var vci = new Vk.ImageViewCreateInfo( (Vk.Format)Format, new Vk.ImageSubresourceRange(VkAspects, 0, 1, 0, 1), viewType: Vk.ImageViewType.Image2D ); VkView = VkImage.CreateView(vci); // Make the initial layout transition Device.SubmitScratchCommand(buf => { buf.CmdPipelineBarrier(Vk.PipelineStages.AllGraphics, Vk.PipelineStages.AllGraphics, imageMemoryBarriers: new[] { new Vk.ImageMemoryBarrier( VkImage, new Vk.ImageSubresourceRange(VkAspects, 0, 1, 0, 1), Vk.Accesses.None, Vk.Accesses.None, Vk.ImageLayout.Undefined, DefaultImageLayout ) }); }); // Build the transitions ClearBarrier = new Vk.ImageMemoryBarrier( VkImage, new Vk.ImageSubresourceRange(VkAspects, 0, 1, 0, 1), Vk.Accesses.None, Vk.Accesses.None, DefaultImageLayout, Vk.ImageLayout.TransferDstOptimal ); AttachBarrier = new Vk.ImageMemoryBarrier( VkImage, new Vk.ImageSubresourceRange(VkAspects, 0, 1, 0, 1), Vk.Accesses.None, Vk.Accesses.None, Vk.ImageLayout.TransferDstOptimal, DefaultImageLayout ); }