Example #1
0
        public static PipelineState New(GraphicsDevice graphicsDevice, ref PipelineStateDescription pipelineStateDescription)
        {
            // Hash the current state
            var hashedState = new PipelineStateDescriptionWithHash(pipelineStateDescription);

            // Store SamplerState in a cache (D3D seems to have quite bad concurrency when using CreateSampler while rendering)
            PipelineState pipelineState;

            lock (graphicsDevice.CachedPipelineStates)
            {
                if (graphicsDevice.CachedPipelineStates.TryGetValue(hashedState, out pipelineState))
                {
                    // TODO: Appropriate destroy
                    pipelineState.AddReferenceInternal();
                }
                else
                {
                    pipelineState = new PipelineState(graphicsDevice, pipelineStateDescription);
                    graphicsDevice.CachedPipelineStates.Add(hashedState, pipelineState);
                }
            }
            return(pipelineState);
        }
Example #2
0
        public void Recreate(GraphicsAdapter adapter, GraphicsProfile[] graphicsProfiles, DeviceCreationFlags deviceCreationFlags, WindowHandle windowHandle)
        {
            if (adapter == null)
            {
                throw new ArgumentNullException("adapter");
            }
            if (graphicsProfiles == null)
            {
                throw new ArgumentNullException("graphicsProfiles");
            }

            Adapter     = adapter;
            IsDebugMode = (deviceCreationFlags & DeviceCreationFlags.Debug) != 0;

            // Default fallback
            if (graphicsProfiles.Length == 0)
            {
                graphicsProfiles = new[] { GraphicsProfile.Level_11_0, GraphicsProfile.Level_10_1, GraphicsProfile.Level_10_0, GraphicsProfile.Level_9_3, GraphicsProfile.Level_9_2, GraphicsProfile.Level_9_1 }
            }
            ;

            // Initialize this instance
            InitializePlatformDevice(graphicsProfiles, deviceCreationFlags, windowHandle);

            // Create a new graphics device
            Features = new GraphicsDeviceFeatures(this);

            SamplerStates = new SamplerStateFactory(this);

            var defaultPipelineStateDescription = new PipelineStateDescription();

            defaultPipelineStateDescription.SetDefaults();
            AdjustDefaultPipelineStateDescription(ref defaultPipelineStateDescription);
            DefaultPipelineState = PipelineState.New(this, ref defaultPipelineStateDescription);

            InitializePostFeatures();
        }
 private void AdjustDefaultPipelineStateDescription(ref PipelineStateDescription pipelineStateDescription)
 {
     // On D3D, default state is Less instead of our LessEqual
     // Let's update default pipeline state so that it correspond to D3D state after a "ClearState()"
     pipelineStateDescription.DepthStencilState.DepthBufferFunction = CompareFunction.Less;
 }
Example #4
0
 private void AdjustDefaultPipelineStateDescription(ref PipelineStateDescription pipelineStateDescription)
 {
 }
 internal PipelineState(GraphicsDevice graphicsDevice, PipelineStateDescription pipelineStateDescription) : base(graphicsDevice)
 {
     Description = pipelineStateDescription.Clone();
     Recreate();
 }
        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),
                            ResourceElementIsInteger = sourceEntry.ElementType != EffectParameterType.Float && sourceEntry.ElementType != EffectParameterType.Double,
                        });
                    }
                }
            }

            // 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 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);
        }
Example #8
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 VkAttachmentDescription[attachmentCount];
            var colorAttachmentReferences = new VkAttachmentReference[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 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(pipelineStateDescription.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,
            };

            vkCreateRenderPass(GraphicsDevice.NativeDevice, &renderPassCreateInfo, null, out NativeRenderPass);
        }
 public PipelineStateDescriptionWithHash(PipelineStateDescription state)
 {
     Hash  = state.GetHashCode();
     State = state;
 }
        internal unsafe PipelineState(GraphicsDevice graphicsDevice, PipelineStateDescription pipelineStateDescription) : base(graphicsDevice)
        {
            if (pipelineStateDescription.RootSignature != null)
            {
                var effectReflection = pipelineStateDescription.EffectBytecode.Reflection;

                var computeShader = pipelineStateDescription.EffectBytecode.Stages.FirstOrDefault(e => e.Stage == ShaderStage.Compute);
                IsCompute = computeShader != null;

                var rootSignatureParameters = new List <RootParameter>();
                var immutableSamplers       = new List <StaticSamplerDescription>();
                SrvBindCounts     = new int[pipelineStateDescription.RootSignature.EffectDescriptorSetReflection.Layouts.Count];
                SamplerBindCounts = new int[pipelineStateDescription.RootSignature.EffectDescriptorSetReflection.Layouts.Count];
                for (int layoutIndex = 0; layoutIndex < pipelineStateDescription.RootSignature.EffectDescriptorSetReflection.Layouts.Count; layoutIndex++)
                {
                    var layout = pipelineStateDescription.RootSignature.EffectDescriptorSetReflection.Layouts[layoutIndex];
                    if (layout.Layout == null)
                    {
                        continue;
                    }

                    // TODO D3D12 for now, we don't control register so we simply generate one resource table per shader stage and per descriptor set layout
                    //            we should switch to a model where we make sure VS/PS don't overlap for common descriptors so that they can be shared
                    var srvDescriptorRanges     = new Dictionary <ShaderStage, List <DescriptorRange> >();
                    var samplerDescriptorRanges = new Dictionary <ShaderStage, List <DescriptorRange> >();

                    int descriptorSrvOffset     = 0;
                    int descriptorSamplerOffset = 0;
                    foreach (var item in layout.Layout.Entries)
                    {
                        var isSampler = item.Class == EffectParameterClass.Sampler;

                        // Find matching resource bindings
                        foreach (var binding in effectReflection.ResourceBindings)
                        {
                            if (binding.Stage == ShaderStage.None || binding.KeyInfo.Key != item.Key)
                            {
                                continue;
                            }

                            List <DescriptorRange> descriptorRanges;
                            {
                                var dictionary = isSampler ? samplerDescriptorRanges : srvDescriptorRanges;
                                if (dictionary.TryGetValue(binding.Stage, out descriptorRanges) == false)
                                {
                                    descriptorRanges = dictionary[binding.Stage] = new List <DescriptorRange>();
                                }
                            }

                            if (isSampler)
                            {
                                if (item.ImmutableSampler != null)
                                {
                                    immutableSamplers.Add(new StaticSamplerDescription(ShaderStage2ShaderVisibility(binding.Stage), binding.SlotStart, 0)
                                    {
                                        // TODO D3D12 ImmutableSampler should only be a state description instead of a GPU object?
                                        Filter         = (Filter)item.ImmutableSampler.Description.Filter,
                                        ComparisonFunc = (Comparison)item.ImmutableSampler.Description.CompareFunction,
                                        BorderColor    = ColorHelper.ConvertStatic(item.ImmutableSampler.Description.BorderColor),
                                        AddressU       = (SharpDX.Direct3D12.TextureAddressMode)item.ImmutableSampler.Description.AddressU,
                                        AddressV       = (SharpDX.Direct3D12.TextureAddressMode)item.ImmutableSampler.Description.AddressV,
                                        AddressW       = (SharpDX.Direct3D12.TextureAddressMode)item.ImmutableSampler.Description.AddressW,
                                        MinLOD         = item.ImmutableSampler.Description.MinMipLevel,
                                        MaxLOD         = item.ImmutableSampler.Description.MaxMipLevel,
                                        MipLODBias     = item.ImmutableSampler.Description.MipMapLevelOfDetailBias,
                                        MaxAnisotropy  = item.ImmutableSampler.Description.MaxAnisotropy,
                                    });
                                }
                                else
                                {
                                    // Add descriptor range
                                    descriptorRanges.Add(new DescriptorRange(DescriptorRangeType.Sampler, item.ArraySize, binding.SlotStart, 0, descriptorSamplerOffset));
                                }
                            }
                            else
                            {
                                DescriptorRangeType descriptorRangeType;
                                switch (binding.Class)
                                {
                                case EffectParameterClass.ConstantBuffer:
                                    descriptorRangeType = DescriptorRangeType.ConstantBufferView;
                                    break;

                                case EffectParameterClass.ShaderResourceView:
                                    descriptorRangeType = DescriptorRangeType.ShaderResourceView;
                                    break;

                                case EffectParameterClass.UnorderedAccessView:
                                    descriptorRangeType = DescriptorRangeType.UnorderedAccessView;
                                    break;

                                default:
                                    throw new NotImplementedException();
                                }

                                // Add descriptor range
                                descriptorRanges.Add(new DescriptorRange(descriptorRangeType, item.ArraySize, binding.SlotStart, 0, descriptorSrvOffset));
                            }
                        }

                        // Move to next element (mirror what is done in DescriptorSetLayout)
                        if (isSampler)
                        {
                            if (item.ImmutableSampler == null)
                            {
                                descriptorSamplerOffset += item.ArraySize;
                            }
                        }
                        else
                        {
                            descriptorSrvOffset += item.ArraySize;
                        }
                    }

                    foreach (var stage in srvDescriptorRanges)
                    {
                        if (stage.Value.Count > 0)
                        {
                            rootSignatureParameters.Add(new RootParameter(ShaderStage2ShaderVisibility(stage.Key), stage.Value.ToArray()));
                            SrvBindCounts[layoutIndex]++;
                        }
                    }
                    foreach (var stage in samplerDescriptorRanges)
                    {
                        if (stage.Value.Count > 0)
                        {
                            rootSignatureParameters.Add(new RootParameter(ShaderStage2ShaderVisibility(stage.Key), stage.Value.ToArray()));
                            SamplerBindCounts[layoutIndex]++;
                        }
                    }
                }
                var rootSignatureDesc = new RootSignatureDescription(RootSignatureFlags.AllowInputAssemblerInputLayout, rootSignatureParameters.ToArray(), immutableSamplers.ToArray());

                var rootSignature = NativeDevice.CreateRootSignature(0, rootSignatureDesc.Serialize());

                InputElement[] inputElements = null;
                if (pipelineStateDescription.InputElements != null)
                {
                    inputElements = new InputElement[pipelineStateDescription.InputElements.Length];
                    for (int i = 0; i < inputElements.Length; ++i)
                    {
                        var inputElement = pipelineStateDescription.InputElements[i];
                        inputElements[i] = new InputElement
                        {
                            Format            = (SharpDX.DXGI.Format)inputElement.Format,
                            AlignedByteOffset = inputElement.AlignedByteOffset,
                            SemanticName      = inputElement.SemanticName,
                            SemanticIndex     = inputElement.SemanticIndex,
                            Slot                 = inputElement.InputSlot,
                            Classification       = (SharpDX.Direct3D12.InputClassification)inputElement.InputSlotClass,
                            InstanceDataStepRate = inputElement.InstanceDataStepRate,
                        };
                    }
                }

                PrimitiveTopologyType primitiveTopologyType;
                switch (pipelineStateDescription.PrimitiveType)
                {
                case PrimitiveType.Undefined:
                    primitiveTopologyType = PrimitiveTopologyType.Undefined;
                    break;

                case PrimitiveType.PointList:
                    primitiveTopologyType = PrimitiveTopologyType.Point;
                    break;

                case PrimitiveType.LineList:
                case PrimitiveType.LineStrip:
                case PrimitiveType.LineListWithAdjacency:
                case PrimitiveType.LineStripWithAdjacency:
                    primitiveTopologyType = PrimitiveTopologyType.Line;
                    break;

                case PrimitiveType.TriangleList:
                case PrimitiveType.TriangleStrip:
                case PrimitiveType.TriangleListWithAdjacency:
                case PrimitiveType.TriangleStripWithAdjacency:
                    primitiveTopologyType = PrimitiveTopologyType.Triangle;
                    break;

                default:
                    if (pipelineStateDescription.PrimitiveType >= PrimitiveType.PatchList && pipelineStateDescription.PrimitiveType < PrimitiveType.PatchList + 32)
                    {
                        primitiveTopologyType = PrimitiveTopologyType.Patch;
                    }
                    else
                    {
                        throw new ArgumentOutOfRangeException("pipelineStateDescription.PrimitiveType");
                    }
                    break;
                }

                // Check if it should use compute pipeline state
                if (IsCompute)
                {
                    var nativePipelineStateDescription = new ComputePipelineStateDescription
                    {
                        ComputeShader        = computeShader.Data,
                        Flags                = PipelineStateFlags.None,
                        RootSignaturePointer = rootSignature,
                    };

                    CompiledState = NativeDevice.CreateComputePipelineState(nativePipelineStateDescription);
                }
                else
                {
                    var nativePipelineStateDescription = new GraphicsPipelineStateDescription
                    {
                        InputLayout        = new InputLayoutDescription(inputElements),
                        RootSignature      = rootSignature,
                        RasterizerState    = CreateRasterizerState(pipelineStateDescription.RasterizerState),
                        BlendState         = CreateBlendState(pipelineStateDescription.BlendState),
                        SampleMask         = (int)pipelineStateDescription.SampleMask,
                        DepthStencilFormat = (SharpDX.DXGI.Format)pipelineStateDescription.Output.DepthStencilFormat,
                        DepthStencilState  = CreateDepthStencilState(pipelineStateDescription.DepthStencilState),
                        RenderTargetCount  = pipelineStateDescription.Output.RenderTargetCount,
                        // TODO D3D12 hardcoded
                        StreamOutput          = new StreamOutputDescription(),
                        PrimitiveTopologyType = primitiveTopologyType,
                        // TODO D3D12 hardcoded
                        SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0),
                    };

                    // Disable depth buffer if no format specified
                    if (nativePipelineStateDescription.DepthStencilFormat == Format.Unknown)
                        nativePipelineStateDescription.DepthStencilState.IsDepthEnabled = false;

                    fixed(PixelFormat *renderTargetFormats = &pipelineStateDescription.Output.RenderTargetFormat0)
                    {
                        for (int i = 0; i < pipelineStateDescription.Output.RenderTargetCount; ++i)
                        {
                            nativePipelineStateDescription.RenderTargetFormats[i] = (SharpDX.DXGI.Format)renderTargetFormats[i];
                        }
                    }

                    foreach (var stage in pipelineStateDescription.EffectBytecode.Stages)
                    {
                        switch (stage.Stage)
                        {
                        case ShaderStage.Vertex:
                            nativePipelineStateDescription.VertexShader = stage.Data;
                            break;

                        case ShaderStage.Hull:
                            nativePipelineStateDescription.HullShader = stage.Data;
                            break;

                        case ShaderStage.Domain:
                            nativePipelineStateDescription.DomainShader = stage.Data;
                            break;

                        case ShaderStage.Geometry:
                            nativePipelineStateDescription.GeometryShader = stage.Data;
                            break;

                        case ShaderStage.Pixel:
                            nativePipelineStateDescription.PixelShader = stage.Data;
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                        }
                    }

                    CompiledState = NativeDevice.CreateGraphicsPipelineState(nativePipelineStateDescription);
                }

                RootSignature     = rootSignature;
                PrimitiveTopology = (PrimitiveTopology)pipelineStateDescription.PrimitiveType;
                HasScissorEnabled = pipelineStateDescription.RasterizerState.ScissorTestEnable;
            }
        }