private unsafe void Recreate() { if (Description.RootSignature == null) { return; } CreateRenderPass(Description); CreatePipelineLayout(Description); // Create shader stages Dictionary <int, string> inputAttributeNames; 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 = 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(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 CreatePipelineLayout(PipelineStateDescription pipelineStateDescription) { // Remap descriptor set indices to those in the shader. This ordering generated by the ShaderCompiler var resourceGroups = pipelineStateDescription.EffectBytecode.Reflection.ResourceBindings.Select(x => x.ResourceGroup ?? "Globals").Distinct().ToList(); ResourceGroupCount = resourceGroups.Count; var layouts = pipelineStateDescription.RootSignature.EffectDescriptorSetReflection.Layouts; // Get binding indices used by the shader var destinationBindings = pipelineStateDescription.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" ? pipelineStateDescription.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 PipelineLayoutCreateInfo { StructureType = StructureType.PipelineLayoutCreateInfo, SetLayoutCount = 1, SetLayouts = new IntPtr(&nativeDescriptorSetLayout) }; NativeLayout = GraphicsDevice.NativeDevice.CreatePipelineLayout(ref pipelineLayoutCreateInfo); }
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 | (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(); }
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(); } } }