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); }
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) }); } } } // 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); }
internal void Prepare(PipelineStateDescription pipelineStateDescription) { 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; } }
internal void Prepare(PipelineStateDescription pipelineStateDescription) { Description = pipelineStateDescription.Clone(); Recreate(); }
public PipelineStateDescriptionWithHash(PipelineStateDescription state) { Hash = state.GetHashCode(); State = state; }
private void AdjustDefaultPipelineStateDescription(ref PipelineStateDescription pipelineStateDescription) { }
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; }
internal void Prepare(PipelineStateDescription pipelineStateDescription) { // First time, build caches var pipelineStateCache = GetPipelineStateCache(); var depthClampEmulation = !pipelineStateDescription.RasterizerState.DepthClipEnable && !GraphicsDevice.HasDepthClamp; #if XENKO_GRAPHICS_API_OPENGLES // Depth Clamp can't be emulated on OpenGL ES 2 (TODO: warning?) if (GraphicsDevice.IsOpenGLES2) { depthClampEmulation = false; } #endif // Store states BlendState = new BlendState(pipelineStateDescription.BlendState, pipelineStateDescription.Output.RenderTargetCount > 0); RasterizerState = new RasterizerState(pipelineStateDescription.RasterizerState); DepthStencilState = new DepthStencilState(pipelineStateDescription.DepthStencilState, pipelineStateDescription.Output.DepthStencilFormat != PixelFormat.None); PrimitiveType = pipelineStateDescription.PrimitiveType.ToOpenGL(); // Compile effect var effectBytecode = pipelineStateDescription.EffectBytecode; EffectProgram = effectBytecode != null?pipelineStateCache.EffectProgramCache.Instantiate(Tuple.Create(effectBytecode, depthClampEmulation)) : null; var rootSignature = pipelineStateDescription.RootSignature; if (rootSignature != null && effectBytecode != null) { ResourceBinder.Compile(GraphicsDevice, rootSignature.EffectDescriptorSetReflection, effectBytecode); } // Vertex attributes if (pipelineStateDescription.InputElements != null) { var vertexAttribs = new List <VertexAttrib>(); foreach (var inputElement in pipelineStateDescription.InputElements) { // Query attribute name from effect var attributeName = "a_" + inputElement.SemanticName + inputElement.SemanticIndex; int attributeIndex; if (!EffectProgram.Attributes.TryGetValue(attributeName, out attributeIndex)) { continue; } var vertexElementFormat = VertexAttrib.ConvertVertexElementFormat(inputElement.Format); vertexAttribs.Add(new VertexAttrib( inputElement.InputSlot, attributeIndex, vertexElementFormat.Size, vertexElementFormat.Type, vertexElementFormat.Normalized, inputElement.AlignedByteOffset)); } VertexAttribs = pipelineStateCache.VertexAttribsCache.Instantiate(vertexAttribs.ToArray()); } ready = true; }