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; } }