Ejemplo n.º 1
0
        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));
        }
Ejemplo n.º 2
0
        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();
        }
Ejemplo n.º 4
0
        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);
            }
        }
Ejemplo n.º 5
0
        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);
        }
Ejemplo n.º 6
0
        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();
                }
            }
        }
Ejemplo n.º 7
0
        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();
            }
        }
Ejemplo n.º 8
0
        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();
        }
Ejemplo n.º 9
0
        //[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);
            }
        }
Ejemplo n.º 10
0
        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);
        }