/// <summary> /// Create a new pipeline with the given description, for use in the given renderer and subpass. /// </summary> /// <param name="description">The description of the pipeline states to build into the pipeline.</param> /// <param name="renderer">The renderer to use the pipeline in.</param> /// <param name="subpass">The subpass index within the renderer to use the pipeline in.</param> public Pipeline(PipelineDescription description, Renderer renderer, uint subpass) : base(ResourceType.Pipeline) { // Create the pipeline handle Handle = CreatePipeline(description, renderer, subpass, out var cache); if (renderer.MSAALayout is not null) { BuildCache = cache; // Only save cache if we might rebuild from MSAA change } // Assign fields Layout = description.Shader !.Layout; Layout.IncRefCount(); ShaderProgram = description.Shader.Program; ShaderProgram.IncRefCount(); Renderer = renderer; Subpass = subpass; // Assign values VertexBindingCount = (uint)cache.VertexBindings.Length; // Register to the renderer renderer.AddPipeline(this); }
// Internal constructor internal Material(ShaderLayout layout, ShaderProgram program, MaterialInput input, MaterialOutput output) { // Assign objects Layout = layout; Program = program; Layout.IncRefCount(); Program.IncRefCount(); Input = input; Output = output; // Validate vertex inputs if (input.Vertices.Count > 0) { uint vmask = 0; foreach (var vd in input.Vertices) { if ((vmask & vd.LocationMask) != 0) { throw new ArgumentException("Duplicate vertex location in descriptions", nameof(input)); } vmask |= vd.LocationMask; } if (vmask != Layout.VertexLocationMask) { throw new ArgumentException( $"Missing vertex location in material descriptions ({vmask:X8} != {Layout.VertexLocationMask:X8})", nameof(input)); } } // Validate outputs if (output.BlendStates.Count != layout.FragmentOutputs.Count) { throw new ArgumentException("Output count mismatch", nameof(output)); } }
// Create a pipeline from a set of build states and a shader private static VkPipeline CreatePipeline(BuildStates states, Renderer renderer, uint subpass, ShaderLayout shaderLayout, ShaderProgram shaderProgram) { var MAIN_STR = stackalloc byte[5] { (byte)'m', (byte)'a', (byte)'i', (byte)'n', (byte)'\0' }; // Color blends var cblends = stackalloc VkPipelineColorBlendAttachmentState[states.ColorBlends.Length]; for (int i = 0; i < states.ColorBlends.Length; ++i) { states.ColorBlends[i].ToVk(out cblends[i]); } VkPipelineColorBlendStateCreateInfo colorBlendCI = new( flags : VkPipelineColorBlendStateCreateFlags.NoFlags, logicOpEnable : false, // TODO: Maybe enable this in the future logicOp : VkLogicOp.Clear, attachmentCount : (uint)states.ColorBlends.Length, attachments : cblends, blendConstants_0 : states.BlendConstants.R, blendConstants_1 : states.BlendConstants.G, blendConstants_2 : states.BlendConstants.B, blendConstants_3 : states.BlendConstants.A ); // Inferred state objects VkPipelineMultisampleStateCreateInfo msaaCI = new( flags : VkPipelineMultisampleStateCreateFlags.NoFlags, rasterizationSamples : (VkSampleCountFlags)renderer.MSAA, sampleShadingEnable : false, // TODO: Allow sample shading minSampleShading : 0, sampleMask : null, alphaToCoverageEnable : false, alphaToOneEnable : false ); // Constant state objects var dynstates = stackalloc VkDynamicState[2] { VkDynamicState.Viewport, VkDynamicState.Scissor }; VkViewport viewport = new(); VkRect2D scissor = new(); VkPipelineViewportStateCreateInfo viewportCI = new( // Dummy value b/c dynamic state flags : VkPipelineViewportStateCreateFlags.NoFlags, viewportCount : 1, viewports : &viewport, scissorCount : 1, scissors : &scissor ); VkPipelineDynamicStateCreateInfo dynamicCI = new( flags : VkPipelineDynamicStateCreateFlags.NoFlags, dynamicStateCount : 2, dynamicStates : dynstates ); // Shader create info var stageCIs = shaderProgram.EnumerateModules().Select(mod => new VkPipelineShaderStageCreateInfo( flags: VkPipelineShaderStageCreateFlags.NoFlags, stage: (VkShaderStageFlags)mod.stage, module: mod.mod, name: MAIN_STR, specializationInfo: null // TODO: Public API for specialization )).ToArray(); VkPipelineTessellationStateCreateInfo.New(out var tessCI); // State cached objects states.DepthStencil.ToVk(out var depthStencilCI); states.VertexInput.ToVk(out var vertexInputCI); states.RasterizerState.ToVk(out var rasterCI); // Create the pipeline fixed(VkPipelineShaderStageCreateInfo *stagePtr = stageCIs) fixed(VkVertexInputAttributeDescription * attributePtr = states.VertexAttributes) fixed(VkVertexInputBindingDescription * bindingPtr = states.VertexBindings) { // Additional create objects VkPipelineVertexInputStateCreateInfo vertexCI = new( flags : VkPipelineVertexInputStateCreateFlags.NoFlags, vertexBindingDescriptionCount : (uint)states.VertexBindings.Length, vertexBindingDescriptions : bindingPtr, vertexAttributeDescriptionCount : (uint)states.VertexAttributes.Length, vertexAttributeDescriptions : attributePtr ); // Create info VkGraphicsPipelineCreateInfo ci = new( flags : VkPipelineCreateFlags.NoFlags, // TODO: see if we can utilize some of the flags stageCount : (uint)stageCIs.Length, stages : stagePtr, vertexInputState : &vertexCI, inputAssemblyState : &vertexInputCI, tessellationState : &tessCI, viewportState : &viewportCI, rasterizationState : &rasterCI, multisampleState : &msaaCI, depthStencilState : &depthStencilCI, colorBlendState : &colorBlendCI, dynamicState : &dynamicCI, layout : shaderLayout.PipelineLayout, renderPass : renderer.RenderPass, subpass : subpass, basePipelineHandle : VulkanHandle <VkPipeline> .Null, basePipelineIndex : 0 ); VulkanHandle <VkPipeline> pipelineHandle; renderer.Graphics.Resources.PipelineCache.CreateGraphicsPipelines(1, &ci, null, &pipelineHandle) .Throw("Failed to recreate pipeline object"); return(new(pipelineHandle, renderer.Graphics.VkDevice)); } }