internal unsafe BufferView GetShaderResourceView(PixelFormat viewFormat) { var createInfo = new BufferViewCreateInfo { StructureType = StructureType.BufferViewCreateInfo, Buffer = NativeBuffer, Format = viewFormat == PixelFormat.None ? Format.Undefined : VulkanConvertExtensions.ConvertPixelFormat(viewFormat), Range = (ulong)SizeInBytes, // this.ElementCount //View = (Description.BufferFlags & BufferFlags.RawBuffer) != 0 ? BufferViewType.Raw : BufferViewType.Formatted }; return(GraphicsDevice.NativeDevice.CreateBufferView(ref createInfo)); }
internal unsafe VkBufferView GetShaderResourceView(PixelFormat viewFormat) { var createInfo = new VkBufferViewCreateInfo { sType = VkStructureType.BufferViewCreateInfo, buffer = NativeBuffer, format = viewFormat == PixelFormat.None ? VkFormat.Undefined : VulkanConvertExtensions.ConvertPixelFormat(viewFormat), range = (ulong)SizeInBytes, // this.ElementCount //view = (Description.BufferFlags & BufferFlags.RawBuffer) != 0 ? VkBufferViewType.Raw : VkBufferViewType.Formatted, }; vkCreateBufferView(GraphicsDevice.NativeDevice, &createInfo, null, out var bufferView); return(bufferView); }
private unsafe void CreateSwapChain() { // we are destroying the swap chain now, because it causes lots of other things to be reset too (like all commandbufferpools) // normally we pass the old swapchain to the create new swapchain Vulkan call... but I haven't figured out a stable way of // preserving the old swap chain to be passed during the new swapchain creation, and then destroying just the old swapchain parts. // might have to reset the command buffers and pipeline stuff after swapchain handoff... for another day e.g. TODO DestroySwapchain(); var formats = new[] { PixelFormat.B8G8R8A8_UNorm_SRgb, PixelFormat.R8G8B8A8_UNorm_SRgb, PixelFormat.B8G8R8A8_UNorm, PixelFormat.R8G8B8A8_UNorm }; foreach (var format in formats) { var nativeFromat = VulkanConvertExtensions.ConvertPixelFormat(format); FormatProperties formatProperties; GraphicsDevice.NativePhysicalDevice.GetFormatProperties(nativeFromat, out formatProperties); if ((formatProperties.OptimalTilingFeatures & FormatFeatureFlags.ColorAttachment) != 0) { Description.BackBufferFormat = format; break; } } // Queue // TODO VULKAN: Queue family is needed when creating the Device, so here we can just do a sanity check? var queueNodeIndex = GraphicsDevice.NativePhysicalDevice.QueueFamilyProperties. Where((properties, index) => (properties.QueueFlags & QueueFlags.Graphics) != 0 && GraphicsDevice.NativePhysicalDevice.GetSurfaceSupport((uint)index, surface)). Select((properties, index) => index).First(); // Surface format var backBufferFormat = VulkanConvertExtensions.ConvertPixelFormat(Description.BackBufferFormat); var surfaceFormats = GraphicsDevice.NativePhysicalDevice.GetSurfaceFormats(surface); if ((surfaceFormats.Length != 1 || surfaceFormats[0].Format != Format.Undefined) && !surfaceFormats.Any(x => x.Format == backBufferFormat)) { backBufferFormat = surfaceFormats[0].Format; } // Create swapchain SurfaceCapabilities surfaceCapabilities; GraphicsDevice.NativePhysicalDevice.GetSurfaceCapabilities(surface, out surfaceCapabilities); // Buffer count uint desiredImageCount = Math.Max(surfaceCapabilities.MinImageCount, 4); if (surfaceCapabilities.MaxImageCount > 0 && desiredImageCount > surfaceCapabilities.MaxImageCount) { desiredImageCount = surfaceCapabilities.MaxImageCount; } // Transform SurfaceTransformFlags preTransform; if ((surfaceCapabilities.SupportedTransforms & SurfaceTransformFlags.Identity) != 0) { preTransform = SurfaceTransformFlags.Identity; } else { preTransform = surfaceCapabilities.CurrentTransform; } // Find present mode var swapChainPresentMode = PresentMode.Fifo; // Always supported, but slow if (Description.PresentationInterval == PresentInterval.Immediate) { var presentModes = GraphicsDevice.NativePhysicalDevice.GetSurfacePresentModes(surface); if (presentModes.Contains(PresentMode.Mailbox)) { swapChainPresentMode = PresentMode.Mailbox; } } // Create swapchain var swapchainCreateInfo = new SwapchainCreateInfo { StructureType = StructureType.SwapchainCreateInfo, Surface = surface, ImageArrayLayers = 1, ImageSharingMode = SharingMode.Exclusive, ImageExtent = new Extent2D((uint)Description.BackBufferWidth, (uint)Description.BackBufferHeight), ImageFormat = backBufferFormat, ImageColorSpace = Description.ColorSpace == ColorSpace.Gamma ? SharpVulkan.ColorSpace.SRgbNonlinear : 0, ImageUsage = ImageUsageFlags.ColorAttachment | ImageUsageFlags.TransferDestination | (surfaceCapabilities.SupportedUsageFlags & ImageUsageFlags.TransferSource), // TODO VULKAN: Use off-screen buffer to emulate PresentMode = swapChainPresentMode, CompositeAlpha = CompositeAlphaFlags.Opaque, MinImageCount = desiredImageCount, PreTransform = preTransform, OldSwapchain = Swapchain.Null, Clipped = true }; swapChain = GraphicsDevice.NativeDevice.CreateSwapchain(ref swapchainCreateInfo); CreateBackBuffers(); // resize/create stencil buffers var newTextureDescription = DepthStencilBuffer.Description; newTextureDescription.Width = Description.BackBufferWidth; newTextureDescription.Height = Description.BackBufferHeight; // Manually update the texture DepthStencilBuffer.OnDestroyed(); // Put it in our back buffer texture DepthStencilBuffer.InitializeFrom(newTextureDescription); // start new presentation thread runPresenter = true; presenterThread = new Thread(new ThreadStart(PresenterThread)); presenterThread.IsBackground = true; presenterThread.Name = "Vulkan Presentation Thread"; presenterThread.Priority = ThreadPriority.AboveNormal; presenterThread.Start(); }
private unsafe void Recreate() { if (Description.RootSignature == null) { return; } CreateRenderPass(Description); CreatePipelineLayout(Description); // Create shader stages Dictionary <int, string> inputAttributeNames; // Note: important to pin this so that stages[x].Name is valid during this whole function void *defaultEntryPointData = Interop.Fixed(defaultEntryPoint); var stages = CreateShaderStages(Description, out inputAttributeNames); var inputAttributes = new VertexInputAttributeDescription[Description.InputElements.Length]; int inputAttributeCount = 0; var inputBindings = new VertexInputBindingDescription[inputAttributes.Length]; int inputBindingCount = 0; for (int inputElementIndex = 0; inputElementIndex < inputAttributes.Length; inputElementIndex++) { var inputElement = Description.InputElements[inputElementIndex]; var slotIndex = inputElement.InputSlot; if (inputElement.InstanceDataStepRate > 1) { throw new NotImplementedException(); } Format format; int size; bool isCompressed; VulkanConvertExtensions.ConvertPixelFormat(inputElement.Format, out format, out size, out isCompressed); var location = inputAttributeNames.FirstOrDefault(x => x.Value == inputElement.SemanticName && inputElement.SemanticIndex == 0 || x.Value == inputElement.SemanticName + inputElement.SemanticIndex); if (location.Value != null) { inputAttributes[inputAttributeCount++] = new VertexInputAttributeDescription { Format = format, Offset = (uint)inputElement.AlignedByteOffset, Binding = (uint)inputElement.InputSlot, Location = (uint)location.Key }; } inputBindings[slotIndex].Binding = (uint)slotIndex; inputBindings[slotIndex].InputRate = inputElement.InputSlotClass == InputClassification.Vertex ? VertexInputRate.Vertex : VertexInputRate.Instance; // TODO VULKAN: This is currently an argument to Draw() overloads. if (inputBindings[slotIndex].Stride < inputElement.AlignedByteOffset + size) { inputBindings[slotIndex].Stride = (uint)(inputElement.AlignedByteOffset + size); } if (inputElement.InputSlot >= inputBindingCount) { inputBindingCount = inputElement.InputSlot + 1; } } var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo { StructureType = StructureType.PipelineInputAssemblyStateCreateInfo, Topology = VulkanConvertExtensions.ConvertPrimitiveType(Description.PrimitiveType), PrimitiveRestartEnable = VulkanConvertExtensions.ConvertPrimitiveRestart(Description.PrimitiveType), }; // TODO VULKAN: Tessellation and multisampling var multisampleState = new PipelineMultisampleStateCreateInfo { StructureType = StructureType.PipelineMultisampleStateCreateInfo, RasterizationSamples = SampleCountFlags.Sample1 }; var tessellationState = new PipelineTessellationStateCreateInfo(); var rasterizationState = CreateRasterizationState(Description.RasterizerState); var depthStencilState = CreateDepthStencilState(Description); var description = Description.BlendState; var renderTargetCount = Description.Output.RenderTargetCount; var colorBlendAttachments = new PipelineColorBlendAttachmentState[renderTargetCount]; var renderTargetBlendState = &description.RenderTarget0; for (int i = 0; i < renderTargetCount; i++) { colorBlendAttachments[i] = new PipelineColorBlendAttachmentState { BlendEnable = renderTargetBlendState->BlendEnable, AlphaBlendOperation = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->AlphaBlendFunction), ColorBlendOperation = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->ColorBlendFunction), DestinationAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaDestinationBlend), DestinationColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorDestinationBlend), SourceAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaSourceBlend), SourceColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorSourceBlend), ColorWriteMask = VulkanConvertExtensions.ConvertColorWriteChannels(renderTargetBlendState->ColorWriteChannels), }; if (description.IndependentBlendEnable) { renderTargetBlendState++; } } var viewportState = new PipelineViewportStateCreateInfo { StructureType = StructureType.PipelineViewportStateCreateInfo, ScissorCount = 1, ViewportCount = 1, }; fixed(DynamicState *dynamicStatesPointer = &dynamicStates[0]) { var vertexInputState = new PipelineVertexInputStateCreateInfo { StructureType = StructureType.PipelineVertexInputStateCreateInfo, VertexAttributeDescriptionCount = (uint)inputAttributeCount, VertexAttributeDescriptions = inputAttributes.Length > 0 ? new IntPtr(Interop.Fixed(inputAttributes)) : IntPtr.Zero, VertexBindingDescriptionCount = (uint)inputBindingCount, VertexBindingDescriptions = inputBindings.Length > 0 ? new IntPtr(Interop.Fixed(inputBindings)) : IntPtr.Zero, }; var colorBlendState = new PipelineColorBlendStateCreateInfo { StructureType = StructureType.PipelineColorBlendStateCreateInfo, AttachmentCount = (uint)renderTargetCount, Attachments = colorBlendAttachments.Length > 0 ? new IntPtr(Interop.Fixed(colorBlendAttachments)) : IntPtr.Zero, }; var dynamicState = new PipelineDynamicStateCreateInfo { StructureType = StructureType.PipelineDynamicStateCreateInfo, DynamicStateCount = (uint)dynamicStates.Length, DynamicStates = new IntPtr(dynamicStatesPointer) }; var createInfo = new GraphicsPipelineCreateInfo { StructureType = StructureType.GraphicsPipelineCreateInfo, Layout = NativeLayout, StageCount = (uint)stages.Length, Stages = stages.Length > 0 ? new IntPtr(Interop.Fixed(stages)) : IntPtr.Zero, //TessellationState = new IntPtr(&tessellationState), VertexInputState = new IntPtr(&vertexInputState), InputAssemblyState = new IntPtr(&inputAssemblyState), RasterizationState = new IntPtr(&rasterizationState), MultisampleState = new IntPtr(&multisampleState), DepthStencilState = new IntPtr(&depthStencilState), ColorBlendState = new IntPtr(&colorBlendState), DynamicState = new IntPtr(&dynamicState), ViewportState = new IntPtr(&viewportState), RenderPass = NativeRenderPass, Subpass = 0, }; NativePipeline = GraphicsDevice.NativeDevice.CreateGraphicsPipelines(PipelineCache.Null, 1, &createInfo); } // Cleanup shader modules foreach (var stage in stages) { GraphicsDevice.NativeDevice.DestroyShaderModule(stage.Module); } }
private unsafe void CreateRenderPass(PipelineStateDescription pipelineStateDescription) { bool hasDepthStencilAttachment = pipelineStateDescription.Output.DepthStencilFormat != PixelFormat.None; var renderTargetCount = pipelineStateDescription.Output.RenderTargetCount; var attachmentCount = renderTargetCount; if (hasDepthStencilAttachment) { attachmentCount++; } var attachments = new AttachmentDescription[attachmentCount]; var colorAttachmentReferences = new AttachmentReference[renderTargetCount]; fixed(PixelFormat *renderTargetFormat = &pipelineStateDescription.Output.RenderTargetFormat0) fixed(BlendStateRenderTargetDescription * blendDescription = &pipelineStateDescription.BlendState.RenderTarget0) { for (int i = 0; i < renderTargetCount; i++) { var currentBlendDesc = pipelineStateDescription.BlendState.IndependentBlendEnable ? (blendDescription + i) : blendDescription; attachments[i] = new AttachmentDescription { Format = VulkanConvertExtensions.ConvertPixelFormat(*(renderTargetFormat + i)), Samples = SampleCountFlags.Sample1, LoadOperation = currentBlendDesc->BlendEnable ? AttachmentLoadOperation.Load : AttachmentLoadOperation.DontCare, // TODO VULKAN: Only if any destination blend? StoreOperation = AttachmentStoreOperation.Store, StencilLoadOperation = AttachmentLoadOperation.DontCare, StencilStoreOperation = AttachmentStoreOperation.DontCare, InitialLayout = ImageLayout.ColorAttachmentOptimal, FinalLayout = ImageLayout.ColorAttachmentOptimal, }; colorAttachmentReferences[i] = new AttachmentReference { Attachment = (uint)i, Layout = ImageLayout.ColorAttachmentOptimal, }; } } if (hasDepthStencilAttachment) { attachments[attachmentCount - 1] = new AttachmentDescription { Format = Texture.GetFallbackDepthStencilFormat(GraphicsDevice, VulkanConvertExtensions.ConvertPixelFormat(pipelineStateDescription.Output.DepthStencilFormat)), Samples = SampleCountFlags.Sample1, LoadOperation = AttachmentLoadOperation.Load, // TODO VULKAN: Only if depth read enabled? StoreOperation = AttachmentStoreOperation.Store, // TODO VULKAN: Only if depth write enabled? StencilLoadOperation = AttachmentLoadOperation.DontCare, // TODO VULKAN: Handle stencil StencilStoreOperation = AttachmentStoreOperation.DontCare, InitialLayout = ImageLayout.DepthStencilAttachmentOptimal, FinalLayout = ImageLayout.DepthStencilAttachmentOptimal, }; } var depthAttachmentReference = new AttachmentReference { Attachment = (uint)attachments.Length - 1, Layout = ImageLayout.DepthStencilAttachmentOptimal, }; var subpass = new SubpassDescription { PipelineBindPoint = PipelineBindPoint.Graphics, ColorAttachmentCount = (uint)renderTargetCount, ColorAttachments = colorAttachmentReferences.Length > 0 ? new IntPtr(Interop.Fixed(colorAttachmentReferences)) : IntPtr.Zero, DepthStencilAttachment = hasDepthStencilAttachment ? new IntPtr(&depthAttachmentReference) : IntPtr.Zero, }; var renderPassCreateInfo = new RenderPassCreateInfo { StructureType = StructureType.RenderPassCreateInfo, AttachmentCount = (uint)attachmentCount, Attachments = attachments.Length > 0 ? new IntPtr(Interop.Fixed(attachments)) : IntPtr.Zero, SubpassCount = 1, Subpasses = new IntPtr(&subpass) }; NativeRenderPass = GraphicsDevice.NativeDevice.CreateRenderPass(ref renderPassCreateInfo); }
private void InitializeFromImpl(DataBox[] dataBoxes = null) { NativeFormat = VulkanConvertExtensions.ConvertPixelFormat(ViewFormat); HasStencil = IsStencilFormat(ViewFormat); NativeImageAspect = IsDepthStencil ? ImageAspectFlags.Depth : ImageAspectFlags.Color; if (HasStencil) { NativeImageAspect |= ImageAspectFlags.Stencil; } // For depth-stencil formats, automatically fall back to a supported one if (IsDepthStencil && HasStencil) { NativeFormat = GetFallbackDepthStencilFormat(GraphicsDevice, NativeFormat); } if (Usage == GraphicsResourceUsage.Staging) { if (NativeImage != SharpVulkan.Image.Null) { throw new InvalidOperationException(); } if (isNotOwningResources) { throw new InvalidOperationException(); } NativeAccessMask = AccessFlags.HostRead | AccessFlags.HostWrite; NativePipelineStageMask = PipelineStageFlags.Host; if (ParentTexture != null) { // Create only a view NativeBuffer = ParentTexture.NativeBuffer; NativeMemory = ParentTexture.NativeMemory; } else { CreateBuffer(); if (dataBoxes != null && dataBoxes.Length > 0) { throw new InvalidOperationException(); } } } else { if (NativeImage != SharpVulkan.Image.Null) { throw new InvalidOperationException(); } NativeLayout = IsRenderTarget ? ImageLayout.ColorAttachmentOptimal : IsDepthStencil ? ImageLayout.DepthStencilAttachmentOptimal : IsShaderResource ? ImageLayout.ShaderReadOnlyOptimal : ImageLayout.General; if (NativeLayout == ImageLayout.TransferDestinationOptimal) { NativeAccessMask = AccessFlags.TransferRead; } if (NativeLayout == ImageLayout.ColorAttachmentOptimal) { NativeAccessMask = AccessFlags.ColorAttachmentWrite; } if (NativeLayout == ImageLayout.DepthStencilAttachmentOptimal) { NativeAccessMask = AccessFlags.DepthStencilAttachmentWrite; } if (NativeLayout == ImageLayout.ShaderReadOnlyOptimal) { NativeAccessMask = AccessFlags.ShaderRead | AccessFlags.InputAttachmentRead; } NativePipelineStageMask = IsRenderTarget ? PipelineStageFlags.ColorAttachmentOutput : IsDepthStencil ? PipelineStageFlags.ColorAttachmentOutput | PipelineStageFlags.EarlyFragmentTests | PipelineStageFlags.LateFragmentTests : IsShaderResource ? PipelineStageFlags.VertexInput | PipelineStageFlags.FragmentShader : PipelineStageFlags.None; if (ParentTexture != null) { // Create only a view NativeImage = ParentTexture.NativeImage; NativeMemory = ParentTexture.NativeMemory; } else { if (!isNotOwningResources) { CreateImage(); InitializeImage(dataBoxes); } } if (!isNotOwningResources) { NativeImageView = GetImageView(ViewType, ArraySlice, MipLevel); NativeColorAttachmentView = GetColorAttachmentView(ViewType, ArraySlice, MipLevel); NativeDepthStencilView = GetDepthStencilView(); } } }
private unsafe void Recreate() { errorDuringCreate = false; if (Description.RootSignature == null) { return; } PipelineShaderStageCreateInfo[] stages; // try to recover from a rare failure? bool retry = false; // it appears pipeline creation is just not thread safe :( lock (PipeLock) { Xenko.Shaders.Compiler.EffectCompilerCache.CompileSynchronization.Wait(); CreateRenderPass(Description); CreatePipelineLayout(Description); // Create shader stages Dictionary <int, string> inputAttributeNames; stages = CreateShaderStages(Description, out inputAttributeNames); var inputAttributes = new VertexInputAttributeDescription[Description.InputElements.Length]; int inputAttributeCount = 0; var inputBindings = new VertexInputBindingDescription[inputAttributes.Length]; int inputBindingCount = 0; for (int inputElementIndex = 0; inputElementIndex < inputAttributes.Length; inputElementIndex++) { var inputElement = Description.InputElements[inputElementIndex]; var slotIndex = inputElement.InputSlot; if (inputElement.InstanceDataStepRate > 1) { throw new NotImplementedException(); } Format format; int size; bool isCompressed; VulkanConvertExtensions.ConvertPixelFormat(inputElement.Format, out format, out size, out isCompressed); var location = inputAttributeNames.FirstOrDefault(x => x.Value == inputElement.SemanticName && inputElement.SemanticIndex == 0 || x.Value == inputElement.SemanticName + inputElement.SemanticIndex); if (location.Value != null) { inputAttributes[inputAttributeCount++] = new VertexInputAttributeDescription { Format = format, Offset = (uint)inputElement.AlignedByteOffset, Binding = (uint)inputElement.InputSlot, Location = (uint)location.Key }; } inputBindings[slotIndex].Binding = (uint)slotIndex; inputBindings[slotIndex].InputRate = inputElement.InputSlotClass == InputClassification.Vertex ? VertexInputRate.Vertex : VertexInputRate.Instance; // TODO VULKAN: This is currently an argument to Draw() overloads. if (inputBindings[slotIndex].Stride < inputElement.AlignedByteOffset + size) { inputBindings[slotIndex].Stride = (uint)(inputElement.AlignedByteOffset + size); } if (inputElement.InputSlot >= inputBindingCount) { inputBindingCount = inputElement.InputSlot + 1; } } var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo { StructureType = StructureType.PipelineInputAssemblyStateCreateInfo, Topology = VulkanConvertExtensions.ConvertPrimitiveType(Description.PrimitiveType), PrimitiveRestartEnable = true, }; // TODO VULKAN: Tessellation and multisampling var multisampleState = new PipelineMultisampleStateCreateInfo { StructureType = StructureType.PipelineMultisampleStateCreateInfo, RasterizationSamples = SampleCountFlags.Sample1 }; //var tessellationState = new PipelineTessellationStateCreateInfo(); var rasterizationState = CreateRasterizationState(Description.RasterizerState); var depthStencilState = CreateDepthStencilState(Description); var description = Description.BlendState; var renderTargetCount = Description.Output.RenderTargetCount; var colorBlendAttachments = new PipelineColorBlendAttachmentState[renderTargetCount]; var renderTargetBlendState = &description.RenderTarget0; for (int i = 0; i < renderTargetCount; i++) { colorBlendAttachments[i] = new PipelineColorBlendAttachmentState { BlendEnable = renderTargetBlendState->BlendEnable, AlphaBlendOperation = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->AlphaBlendFunction), ColorBlendOperation = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->ColorBlendFunction), DestinationAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaDestinationBlend), DestinationColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorDestinationBlend), SourceAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaSourceBlend), SourceColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorSourceBlend), ColorWriteMask = VulkanConvertExtensions.ConvertColorWriteChannels(renderTargetBlendState->ColorWriteChannels), }; if (description.IndependentBlendEnable) { renderTargetBlendState++; } } var viewportState = new PipelineViewportStateCreateInfo { StructureType = StructureType.PipelineViewportStateCreateInfo, ScissorCount = 1, ViewportCount = 1, }; fixed(void *dynamicStatesPointer = dynamicStates.Length == 0?null : dynamicStates, inputAttributesPointer = inputAttributes.Length == 0?null : inputAttributes, inputBindingsPointer = inputBindings.Length == 0?null : inputBindings, colorBlendAttachmentsPointer = colorBlendAttachments.Length == 0?null : colorBlendAttachments, stagesPointer = stages.Length == 0?null : stages) { var vertexInputState = new PipelineVertexInputStateCreateInfo { StructureType = StructureType.PipelineVertexInputStateCreateInfo, VertexAttributeDescriptionCount = (uint)inputAttributeCount, VertexAttributeDescriptions = (IntPtr)inputAttributesPointer, VertexBindingDescriptionCount = (uint)inputBindingCount, VertexBindingDescriptions = (IntPtr)inputBindingsPointer, }; var colorBlendState = new PipelineColorBlendStateCreateInfo { StructureType = StructureType.PipelineColorBlendStateCreateInfo, AttachmentCount = (uint)renderTargetCount, Attachments = (IntPtr)colorBlendAttachmentsPointer, }; var dynamicState = new PipelineDynamicStateCreateInfo { StructureType = StructureType.PipelineDynamicStateCreateInfo, DynamicStateCount = (uint)dynamicStates.Length, DynamicStates = (IntPtr)dynamicStatesPointer, }; var createInfo = new GraphicsPipelineCreateInfo { StructureType = StructureType.GraphicsPipelineCreateInfo, Layout = NativeLayout, StageCount = (uint)stages.Length, Stages = (IntPtr)stagesPointer, //TessellationState = new IntPtr(&tessellationState), VertexInputState = new IntPtr(&vertexInputState), InputAssemblyState = new IntPtr(&inputAssemblyState), RasterizationState = new IntPtr(&rasterizationState), MultisampleState = new IntPtr(&multisampleState), DepthStencilState = new IntPtr(&depthStencilState), ColorBlendState = new IntPtr(&colorBlendState), DynamicState = new IntPtr(&dynamicState), ViewportState = new IntPtr(&viewportState), RenderPass = NativeRenderPass, Subpass = 0, }; try { lock (GraphicsDevice.QueueLock) { NativePipeline = GraphicsDevice.NativeDevice.CreateGraphicsPipelines(PipelineCache.Null, 1, &createInfo); } } catch (AccessViolationException ae) { // this happens extremely rarely and might be recoverable by reattempting the recreate retry = true; } catch (Exception e) { errorDuringCreate = true; NativePipeline = Pipeline.Null; } } } // Cleanup shader modules for (int i = 0; i < stages.Length; i++) { GraphicsDevice.NativeDevice.DestroyShaderModule(stages[i].Module); } if (retry) { Recreate(); } }
private unsafe void CreateSwapChain() { var formats = new[] { PixelFormat.B8G8R8A8_UNorm_SRgb, PixelFormat.R8G8B8A8_UNorm_SRgb, PixelFormat.B8G8R8A8_UNorm, PixelFormat.R8G8B8A8_UNorm }; foreach (var format in formats) { var nativeFromat = VulkanConvertExtensions.ConvertPixelFormat(format); FormatProperties formatProperties; GraphicsDevice.NativePhysicalDevice.GetFormatProperties(nativeFromat, out formatProperties); if ((formatProperties.OptimalTilingFeatures & FormatFeatureFlags.ColorAttachment) != 0) { Description.BackBufferFormat = format; break; } } // Queue // TODO VULKAN: Queue family is needed when creating the Device, so here we can just do a sanity check? var queueNodeIndex = GraphicsDevice.NativePhysicalDevice.QueueFamilyProperties. Where((properties, index) => (properties.QueueFlags & QueueFlags.Graphics) != 0 && GraphicsDevice.NativePhysicalDevice.GetSurfaceSupport((uint)index, surface)). Select((properties, index) => index).First(); // Surface format var backBufferFormat = VulkanConvertExtensions.ConvertPixelFormat(Description.BackBufferFormat); var surfaceFormats = GraphicsDevice.NativePhysicalDevice.GetSurfaceFormats(surface); if ((surfaceFormats.Length != 1 || surfaceFormats[0].Format != Format.Undefined) && !surfaceFormats.Any(x => x.Format == backBufferFormat)) { backBufferFormat = surfaceFormats[0].Format; } // Create swapchain SurfaceCapabilities surfaceCapabilities; GraphicsDevice.NativePhysicalDevice.GetSurfaceCapabilities(surface, out surfaceCapabilities); // Buffer count uint desiredImageCount = Math.Max(surfaceCapabilities.MinImageCount, 2); if (surfaceCapabilities.MaxImageCount > 0 && desiredImageCount > surfaceCapabilities.MaxImageCount) { desiredImageCount = surfaceCapabilities.MaxImageCount; } // Transform SurfaceTransformFlags preTransform; if ((surfaceCapabilities.SupportedTransforms & SurfaceTransformFlags.Identity) != 0) { preTransform = SurfaceTransformFlags.Identity; } else { preTransform = surfaceCapabilities.CurrentTransform; } // Find present mode var presentModes = GraphicsDevice.NativePhysicalDevice.GetSurfacePresentModes(surface); var swapChainPresentMode = PresentMode.Fifo; // Always supported foreach (var presentMode in presentModes) { // TODO VULKAN: Handle PresentInterval.Two if (Description.PresentationInterval == PresentInterval.Immediate) { // Prefer mailbox to immediate if (presentMode == PresentMode.Immediate) { swapChainPresentMode = PresentMode.Immediate; } else if (presentMode == PresentMode.Mailbox) { swapChainPresentMode = PresentMode.Mailbox; break; } } } // Create swapchain var swapchainCreateInfo = new SwapchainCreateInfo { StructureType = StructureType.SwapchainCreateInfo, Surface = surface, ImageArrayLayers = 1, ImageSharingMode = SharingMode.Exclusive, ImageExtent = new Extent2D((uint)Description.BackBufferWidth, (uint)Description.BackBufferHeight), ImageFormat = backBufferFormat, ImageColorSpace = Description.ColorSpace == ColorSpace.Gamma ? SharpVulkan.ColorSpace.SRgbNonlinear : 0, ImageUsage = ImageUsageFlags.ColorAttachment | ImageUsageFlags.TransferDestination | (surfaceCapabilities.SupportedUsageFlags & ImageUsageFlags.TransferSource), // TODO VULKAN: Use off-screen buffer to emulate PresentMode = swapChainPresentMode, CompositeAlpha = CompositeAlphaFlags.Opaque, MinImageCount = desiredImageCount, PreTransform = preTransform, OldSwapchain = swapChain, Clipped = true }; var newSwapChain = GraphicsDevice.NativeDevice.CreateSwapchain(ref swapchainCreateInfo); DestroySwapchain(); swapChain = newSwapChain; CreateBackBuffers(); }
//[HandleProcessCorruptedStateExceptionsAttribute, SecurityCriticalAttribute] private unsafe void Recreate() { errorDuringCreate = false; if (Description.RootSignature == null) { return; } VkPipelineShaderStageCreateInfo[] stages; // create render pass bool hasDepthStencilAttachment = Description.Output.DepthStencilFormat != PixelFormat.None; var renderTargetCount = Description.Output.RenderTargetCount; var attachmentCount = renderTargetCount; if (hasDepthStencilAttachment) { attachmentCount++; } var attachments = new VkAttachmentDescription[attachmentCount]; var colorAttachmentReferences = new VkAttachmentReference[renderTargetCount]; fixed(PixelFormat *renderTargetFormat = &Description.Output.RenderTargetFormat0) fixed(BlendStateRenderTargetDescription * blendDescription = &Description.BlendState.RenderTarget0) { for (int i = 0; i < renderTargetCount; i++) { var currentBlendDesc = Description.BlendState.IndependentBlendEnable ? (blendDescription + i) : blendDescription; attachments[i] = new VkAttachmentDescription { format = VulkanConvertExtensions.ConvertPixelFormat(*(renderTargetFormat + i)), samples = VkSampleCountFlags.Count1, loadOp = currentBlendDesc->BlendEnable ? VkAttachmentLoadOp.Load : VkAttachmentLoadOp.DontCare, // TODO VULKAN: Only if any destination blend? storeOp = VkAttachmentStoreOp.Store, stencilLoadOp = VkAttachmentLoadOp.DontCare, stencilStoreOp = VkAttachmentStoreOp.DontCare, initialLayout = VkImageLayout.ColorAttachmentOptimal, finalLayout = VkImageLayout.ColorAttachmentOptimal, }; colorAttachmentReferences[i] = new VkAttachmentReference { attachment = (uint)i, layout = VkImageLayout.ColorAttachmentOptimal, }; } } if (hasDepthStencilAttachment) { attachments[attachmentCount - 1] = new VkAttachmentDescription { format = Texture.GetFallbackDepthStencilFormat(GraphicsDevice, VulkanConvertExtensions.ConvertPixelFormat(Description.Output.DepthStencilFormat)), samples = VkSampleCountFlags.Count1, loadOp = VkAttachmentLoadOp.Load, // TODO VULKAN: Only if depth read enabled? storeOp = VkAttachmentStoreOp.Store, // TODO VULKAN: Only if depth write enabled? stencilLoadOp = VkAttachmentLoadOp.DontCare, // TODO VULKAN: Handle stencil stencilStoreOp = VkAttachmentStoreOp.DontCare, initialLayout = VkImageLayout.DepthStencilAttachmentOptimal, finalLayout = VkImageLayout.DepthStencilAttachmentOptimal, }; } var depthAttachmentReference = new VkAttachmentReference { attachment = (uint)attachments.Length - 1, layout = VkImageLayout.DepthStencilAttachmentOptimal, }; var subpass = new VkSubpassDescription { pipelineBindPoint = VkPipelineBindPoint.Graphics, colorAttachmentCount = (uint)renderTargetCount, pColorAttachments = colorAttachmentReferences.Length > 0 ? (VkAttachmentReference *)Core.Interop.Fixed(colorAttachmentReferences) : null, pDepthStencilAttachment = hasDepthStencilAttachment ? &depthAttachmentReference : null, }; var renderPassCreateInfo = new VkRenderPassCreateInfo { sType = VkStructureType.RenderPassCreateInfo, attachmentCount = (uint)attachmentCount, pAttachments = attachments.Length > 0 ? (VkAttachmentDescription *)Core.Interop.Fixed(attachments) : null, subpassCount = 1, pSubpasses = &subpass, }; // create pipeline layout // Remap descriptor set indices to those in the shader. This ordering generated by the ShaderCompiler var resourceGroups = Description.EffectBytecode.Reflection.ResourceBindings.Select(x => x.ResourceGroup ?? "Globals").Distinct().ToList(); ResourceGroupCount = resourceGroups.Count; var layouts = Description.RootSignature.EffectDescriptorSetReflection.Layouts; // Get binding indices used by the shader var destinationBindings = Description.EffectBytecode.Stages .SelectMany(x => BinarySerialization.Read <ShaderInputBytecode>(x.Data).ResourceBindings) .GroupBy(x => x.Key, x => x.Value) .ToDictionary(x => x.Key, x => x.First()); var maxBindingIndex = destinationBindings.Max(x => x.Value); var destinationEntries = new DescriptorSetLayoutBuilder.Entry[maxBindingIndex + 1]; DescriptorBindingMapping = new List <DescriptorSetInfo>(); for (int i = 0; i < resourceGroups.Count; i++) { var resourceGroupName = resourceGroups[i] == "Globals" ? Description.RootSignature.EffectDescriptorSetReflection.DefaultSetSlot : resourceGroups[i]; var layoutIndex = resourceGroups[i] == null ? 0 : layouts.FindIndex(x => x.Name == resourceGroupName); // Check if the resource group is used by the shader if (layoutIndex == -1) { continue; } var sourceEntries = layouts[layoutIndex].Layout.Entries; for (int sourceBinding = 0; sourceBinding < sourceEntries.Count; sourceBinding++) { var sourceEntry = sourceEntries[sourceBinding]; int destinationBinding; if (destinationBindings.TryGetValue(sourceEntry.Key.Name, out destinationBinding)) { destinationEntries[destinationBinding] = sourceEntry; // No need to umpdate immutable samplers if (sourceEntry.Class == EffectParameterClass.Sampler && sourceEntry.ImmutableSampler != null) { continue; } DescriptorBindingMapping.Add(new DescriptorSetInfo { SourceSet = layoutIndex, SourceBinding = sourceBinding, DestinationBinding = destinationBinding, DescriptorType = VulkanConvertExtensions.ConvertDescriptorType(sourceEntry.Class, sourceEntry.Type) }); } } } // Create default sampler, used by texture and buffer loads destinationEntries[0] = new DescriptorSetLayoutBuilder.Entry { Class = EffectParameterClass.Sampler, Type = EffectParameterType.Sampler, ImmutableSampler = GraphicsDevice.SamplerStates.PointWrap, ArraySize = 1, }; // Create descriptor set layout NativeDescriptorSetLayout = DescriptorSetLayout.CreateNativeDescriptorSetLayout(GraphicsDevice, destinationEntries, out DescriptorTypeCounts); // Create pipeline layout var nativeDescriptorSetLayout = NativeDescriptorSetLayout; var pipelineLayoutCreateInfo = new VkPipelineLayoutCreateInfo { sType = VkStructureType.PipelineLayoutCreateInfo, setLayoutCount = 1, pSetLayouts = &nativeDescriptorSetLayout, }; // Create shader stages Dictionary <int, string> inputAttributeNames; // Note: important to pin this so that stages[x].Name is valid during this whole function void *defaultEntryPointData = Core.Interop.Fixed(defaultEntryPoint); stages = CreateShaderStages(Description, out inputAttributeNames); var inputAttributes = new VkVertexInputAttributeDescription[Description.InputElements.Length]; int inputAttributeCount = 0; var inputBindings = new VkVertexInputBindingDescription[inputAttributes.Length]; int inputBindingCount = 0; for (int inputElementIndex = 0; inputElementIndex < inputAttributes.Length; inputElementIndex++) { var inputElement = Description.InputElements[inputElementIndex]; var slotIndex = inputElement.InputSlot; if (inputElement.InstanceDataStepRate > 1) { throw new NotImplementedException(); } VkFormat format; int size; bool isCompressed; VulkanConvertExtensions.ConvertPixelFormat(inputElement.Format, out format, out size, out isCompressed); var location = inputAttributeNames.FirstOrDefault(x => x.Value == inputElement.SemanticName && inputElement.SemanticIndex == 0 || x.Value == inputElement.SemanticName + inputElement.SemanticIndex); if (location.Value != null) { inputAttributes[inputAttributeCount++] = new VkVertexInputAttributeDescription { format = format, offset = (uint)inputElement.AlignedByteOffset, binding = (uint)inputElement.InputSlot, location = (uint)location.Key }; } inputBindings[slotIndex].binding = (uint)slotIndex; inputBindings[slotIndex].inputRate = inputElement.InputSlotClass == InputClassification.Vertex ? VkVertexInputRate.Vertex : VkVertexInputRate.Instance; // TODO VULKAN: This is currently an argument to Draw() overloads. if (inputBindings[slotIndex].stride < inputElement.AlignedByteOffset + size) { inputBindings[slotIndex].stride = (uint)(inputElement.AlignedByteOffset + size); } if (inputElement.InputSlot >= inputBindingCount) { inputBindingCount = inputElement.InputSlot + 1; } } var inputAssemblyState = new VkPipelineInputAssemblyStateCreateInfo { sType = VkStructureType.PipelineInputAssemblyStateCreateInfo, topology = VulkanConvertExtensions.ConvertPrimitiveType(Description.PrimitiveType), primitiveRestartEnable = VulkanConvertExtensions.ConvertPrimitiveRestart(Description.PrimitiveType) ? (uint)1 : (uint)0, }; // TODO VULKAN: Tessellation and multisampling var multisampleState = new VkPipelineMultisampleStateCreateInfo { sType = VkStructureType.PipelineMultisampleStateCreateInfo, rasterizationSamples = VkSampleCountFlags.Count1, }; var rasterizationState = new VkPipelineRasterizationStateCreateInfo { sType = VkStructureType.PipelineRasterizationStateCreateInfo, cullMode = VulkanConvertExtensions.ConvertCullMode(Description.RasterizerState.CullMode), frontFace = Description.RasterizerState.FrontFaceCounterClockwise ? VkFrontFace.CounterClockwise : VkFrontFace.Clockwise, polygonMode = VulkanConvertExtensions.ConvertFillMode(Description.RasterizerState.FillMode), depthBiasEnable = 1, // TODO VULKAN depthBiasConstantFactor = Description.RasterizerState.DepthBias, depthBiasSlopeFactor = Description.RasterizerState.SlopeScaleDepthBias, depthBiasClamp = Description.RasterizerState.DepthBiasClamp, lineWidth = 1.0f, depthClampEnable = Description.RasterizerState.DepthClipEnable ? (uint)0 : (uint)1, rasterizerDiscardEnable = 0, }; var depthStencilState = new VkPipelineDepthStencilStateCreateInfo { sType = VkStructureType.PipelineDepthStencilStateCreateInfo, depthTestEnable = Description.DepthStencilState.DepthBufferEnable ? (uint)1 : (uint)0, stencilTestEnable = Description.DepthStencilState.StencilEnable ? (uint)1 : (uint)0, depthWriteEnable = Description.DepthStencilState.DepthBufferWriteEnable ? (uint)1 : (uint)0, minDepthBounds = 0.0f, maxDepthBounds = 1.0f, depthCompareOp = VulkanConvertExtensions.ConvertComparisonFunction(Description.DepthStencilState.DepthBufferFunction), front = { compareOp = VulkanConvertExtensions.ConvertComparisonFunction(Description.DepthStencilState.FrontFace.StencilFunction), depthFailOp = VulkanConvertExtensions.ConvertStencilOperation(Description.DepthStencilState.FrontFace.StencilDepthBufferFail), failOp = VulkanConvertExtensions.ConvertStencilOperation(Description.DepthStencilState.FrontFace.StencilFail), passOp = VulkanConvertExtensions.ConvertStencilOperation(Description.DepthStencilState.FrontFace.StencilPass), compareMask = Description.DepthStencilState.StencilMask, writeMask = Description.DepthStencilState.StencilWriteMask }, back = { compareOp = VulkanConvertExtensions.ConvertComparisonFunction(Description.DepthStencilState.BackFace.StencilFunction), depthFailOp = VulkanConvertExtensions.ConvertStencilOperation(Description.DepthStencilState.BackFace.StencilDepthBufferFail), failOp = VulkanConvertExtensions.ConvertStencilOperation(Description.DepthStencilState.BackFace.StencilFail), passOp = VulkanConvertExtensions.ConvertStencilOperation(Description.DepthStencilState.BackFace.StencilPass), compareMask = Description.DepthStencilState.StencilMask, writeMask = Description.DepthStencilState.StencilWriteMask } }; var description = Description.BlendState; var colorBlendAttachments = new VkPipelineColorBlendAttachmentState[renderTargetCount]; var renderTargetBlendState = &description.RenderTarget0; for (int i = 0; i < renderTargetCount; i++) { colorBlendAttachments[i] = new VkPipelineColorBlendAttachmentState { blendEnable = renderTargetBlendState->BlendEnable ? (uint)1 : (uint)0, alphaBlendOp = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->AlphaBlendFunction), colorBlendOp = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->ColorBlendFunction), dstAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaDestinationBlend), dstColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorDestinationBlend), srcAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaSourceBlend), srcColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorSourceBlend), colorWriteMask = VulkanConvertExtensions.ConvertColorWriteChannels(renderTargetBlendState->ColorWriteChannels), }; if (description.IndependentBlendEnable) { renderTargetBlendState++; } } var viewportState = new VkPipelineViewportStateCreateInfo { sType = VkStructureType.PipelineViewportStateCreateInfo, scissorCount = 1, viewportCount = 1, }; fixed(void *dynamicStatesPointer = dynamicStates.Length == 0?null : dynamicStates, inputAttributesPointer = inputAttributes.Length == 0?null : inputAttributes, inputBindingsPointer = inputBindings.Length == 0?null : inputBindings, colorBlendAttachmentsPointer = colorBlendAttachments.Length == 0?null : colorBlendAttachments, stagesPointer = stages.Length == 0?null : stages) { var vertexInputState = new VkPipelineVertexInputStateCreateInfo { sType = VkStructureType.PipelineVertexInputStateCreateInfo, vertexAttributeDescriptionCount = (uint)inputAttributeCount, pVertexAttributeDescriptions = (Vortice.Vulkan.VkVertexInputAttributeDescription *)inputAttributesPointer, vertexBindingDescriptionCount = (uint)inputBindingCount, pVertexBindingDescriptions = (Vortice.Vulkan.VkVertexInputBindingDescription *)inputBindingsPointer, }; var colorBlendState = new VkPipelineColorBlendStateCreateInfo { sType = VkStructureType.PipelineColorBlendStateCreateInfo, attachmentCount = (uint)renderTargetCount, pAttachments = (Vortice.Vulkan.VkPipelineColorBlendAttachmentState *)colorBlendAttachmentsPointer, }; var dynamicState = new VkPipelineDynamicStateCreateInfo { sType = VkStructureType.PipelineDynamicStateCreateInfo, dynamicStateCount = (uint)dynamicStates.Length, pDynamicStates = (Vortice.Vulkan.VkDynamicState *)dynamicStatesPointer, }; var createInfo = new VkGraphicsPipelineCreateInfo { sType = VkStructureType.GraphicsPipelineCreateInfo, layout = NativeLayout, stageCount = (uint)stages.Length, pVertexInputState = &vertexInputState, pInputAssemblyState = &inputAssemblyState, pRasterizationState = &rasterizationState, pMultisampleState = &multisampleState, pDepthStencilState = &depthStencilState, pColorBlendState = &colorBlendState, pDynamicState = &dynamicState, pStages = (Vortice.Vulkan.VkPipelineShaderStageCreateInfo *)stagesPointer, pViewportState = &viewportState, renderPass = NativeRenderPass, subpass = 0, }; using (GraphicsDevice.QueueLock.ReadLock()) { vkCreateRenderPass(GraphicsDevice.NativeDevice, &renderPassCreateInfo, null, out NativeRenderPass); vkCreatePipelineLayout(GraphicsDevice.NativeDevice, &pipelineLayoutCreateInfo, null, out NativeLayout); createInfo.layout = NativeLayout; createInfo.renderPass = NativeRenderPass; try { fixed(VkPipeline *nativePipelinePtr = &NativePipeline) vkCreateGraphicsPipelines(GraphicsDevice.NativeDevice, VkPipelineCache.Null, 1, &createInfo, null, nativePipelinePtr); } catch (Exception e) { errorDuringCreate = true; NativePipeline = VkPipeline.Null; } } } // Cleanup shader modules for (int i = 0; i < stages.Length; i++) { vkDestroyShaderModule(GraphicsDevice.NativeDevice, stages[i].module, null); } }
private unsafe void CreateSwapChain() { // we are destroying the swap chain now, because it causes lots of other things to be reset too (like all commandbufferpools) // normally we pass the old swapchain to the create new swapchain Vulkan call... but I haven't figured out a stable way of // preserving the old swap chain to be passed during the new swapchain creation, and then destroying just the old swapchain parts. // might have to reset the command buffers and pipeline stuff after swapchain handoff... for another day e.g. TODO DestroySwapchain(); var formats = new[] { PixelFormat.B8G8R8A8_UNorm_SRgb, PixelFormat.R8G8B8A8_UNorm_SRgb, PixelFormat.B8G8R8A8_UNorm, PixelFormat.R8G8B8A8_UNorm }; foreach (var format in formats) { var nativeFromat = VulkanConvertExtensions.ConvertPixelFormat(format); vkGetPhysicalDeviceFormatProperties(GraphicsDevice.NativePhysicalDevice, nativeFromat, out var formatProperties); if ((formatProperties.optimalTilingFeatures & VkFormatFeatureFlags.ColorAttachment) != 0) { Description.BackBufferFormat = format; break; } } // Queue // TODO VULKAN: Queue family is needed when creating the Device, so here we can just do a sanity check? var queueNodeIndex = vkGetPhysicalDeviceQueueFamilyProperties(GraphicsDevice.NativePhysicalDevice).ToArray(). Where((properties, index) => (properties.queueFlags & VkQueueFlags.Graphics) != 0 && vkGetPhysicalDeviceSurfaceSupportKHR(GraphicsDevice.NativePhysicalDevice, (uint)index, surface, out var supported) == VkResult.Success && supported == Vortice.Vulkan.VkBool32.True). Select((properties, index) => index).First(); // Surface format var backBufferFormat = VulkanConvertExtensions.ConvertPixelFormat(Description.BackBufferFormat); var surfaceFormats = vkGetPhysicalDeviceSurfaceFormatsKHR(GraphicsDevice.NativePhysicalDevice, surface).ToArray(); if ((surfaceFormats.Length != 1 || surfaceFormats[0].format != VkFormat.Undefined) && !surfaceFormats.Any(x => x.format == backBufferFormat)) { backBufferFormat = surfaceFormats[0].format; } // Create swapchain vkGetPhysicalDeviceSurfaceCapabilitiesKHR(GraphicsDevice.NativePhysicalDevice, surface, out var surfaceCapabilities); // Buffer count uint desiredImageCount = Math.Max(surfaceCapabilities.minImageCount, 6); if (surfaceCapabilities.maxImageCount > 0 && desiredImageCount > surfaceCapabilities.maxImageCount) { desiredImageCount = surfaceCapabilities.maxImageCount; } // Transform VkSurfaceTransformFlagsKHR preTransform; if ((surfaceCapabilities.supportedTransforms & VkSurfaceTransformFlagsKHR.Identity) != 0) { preTransform = VkSurfaceTransformFlagsKHR.Identity; } else { preTransform = surfaceCapabilities.currentTransform; } // Find present mode var swapChainPresentMode = VkPresentModeKHR.Fifo; // Always supported, but slow if (Description.PresentationInterval == PresentInterval.Immediate) { var presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(GraphicsDevice.NativePhysicalDevice, surface); foreach (var pm in presentModes) { if (pm == VkPresentModeKHR.Mailbox) { swapChainPresentMode = VkPresentModeKHR.Mailbox; break; } } } // Create swapchain var swapchainCreateInfo = new VkSwapchainCreateInfoKHR { sType = VkStructureType.SwapchainCreateInfoKHR, surface = surface, imageArrayLayers = 1, imageSharingMode = VkSharingMode.Exclusive, imageExtent = new Vortice.Vulkan.VkExtent2D(Description.BackBufferWidth, Description.BackBufferHeight), imageFormat = backBufferFormat, imageColorSpace = Description.ColorSpace == ColorSpace.Gamma ? VkColorSpaceKHR.SrgbNonLinear : 0, imageUsage = VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferDst | (surfaceCapabilities.supportedUsageFlags & VkImageUsageFlags.TransferSrc), // TODO VULKAN: Use off-screen buffer to emulate presentMode = swapChainPresentMode, compositeAlpha = VkCompositeAlphaFlagsKHR.Opaque, minImageCount = desiredImageCount, preTransform = preTransform, oldSwapchain = swapChain, clipped = Vortice.Vulkan.VkBool32.True }; vkCreateSwapchainKHR(GraphicsDevice.NativeDevice, &swapchainCreateInfo, null, out swapChain); CreateBackBuffers(); // resize/create stencil buffers var newTextureDescription = DepthStencilBuffer.Description; newTextureDescription.Width = Description.BackBufferWidth; newTextureDescription.Height = Description.BackBufferHeight; // Manually update the texture DepthStencilBuffer.OnDestroyed(); // Put it in our back buffer texture DepthStencilBuffer.InitializeFrom(newTextureDescription); }