- Push constants: rotation angle (float) sent to vertex shader via vkCmdPushConstants - UBO + descriptor sets: VP matrix (mat4) in uniform buffer, bound via VkDescriptorSet - New Vulkan functions: vkCreateDescriptorSetLayout, vkCreateDescriptorPool, vkAllocateDescriptorSets, vkUpdateDescriptorSets, vkCmdBindDescriptorSets, vkCmdPushConstants - New structs: VkPushConstantRange, VkDescriptorSetLayoutBinding, VkDescriptorSetLayoutCreateInfo, VkDescriptorPoolCreateInfo, VkDescriptorPoolSize, VkDescriptorSetAllocateInfo, VkWriteDescriptorSet, VkDescriptorBufferInfo - Per-frame UBO buffers (HOST_VISIBLE|HOST_COHERENT, 64 bytes each) - Perspective projection + look-at camera (static, no ECS yet) - Cleaned up debug logging in VulkanContext - Zero validation errors
258 lines
9.6 KiB
C#
258 lines
9.6 KiB
C#
using System.Runtime.InteropServices;
|
|
using Engine.Core;
|
|
|
|
namespace Engine.Graphics.Vulkan;
|
|
|
|
internal sealed unsafe class VulkanPipeline : IDisposable
|
|
{
|
|
public VkPipelineLayout PipelineLayout;
|
|
public VkPipeline Pipeline;
|
|
public VkShaderModule VertModule;
|
|
public VkShaderModule FragModule;
|
|
public VkDescriptorSetLayout DescriptorSetLayout;
|
|
|
|
private readonly VkDevice _device;
|
|
private bool _disposed;
|
|
|
|
public VulkanPipeline(VkDevice device, VkFormat colorFormat, byte[] vertSpv, byte[] fragSpv)
|
|
{
|
|
_device = device;
|
|
VertModule = CreateShaderModule(vertSpv);
|
|
FragModule = CreateShaderModule(fragSpv);
|
|
|
|
CreateDescriptorSetLayout();
|
|
|
|
fixed (byte* pName = "main\0"u8)
|
|
{
|
|
var stages = stackalloc VkPipelineShaderStageCreateInfo[2];
|
|
stages[0] = new VkPipelineShaderStageCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineShaderStageCreateInfo,
|
|
stage = VkShaderStageFlags.Vertex,
|
|
module = VertModule,
|
|
pName = pName,
|
|
};
|
|
stages[1] = new VkPipelineShaderStageCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineShaderStageCreateInfo,
|
|
stage = VkShaderStageFlags.Fragment,
|
|
module = FragModule,
|
|
pName = pName,
|
|
};
|
|
|
|
var bindings = stackalloc VkVertexInputBindingDescription[1];
|
|
bindings[0] = new VkVertexInputBindingDescription
|
|
{
|
|
binding = 0,
|
|
stride = (uint)sizeof(Vertex),
|
|
inputRate = VkVertexInputRate.Vertex,
|
|
};
|
|
|
|
var attributes = stackalloc VkVertexInputAttributeDescription[3];
|
|
attributes[0] = new VkVertexInputAttributeDescription
|
|
{
|
|
location = 0,
|
|
binding = 0,
|
|
format = VkFormat.R32G32B32Sfloat,
|
|
offset = 0,
|
|
};
|
|
attributes[1] = new VkVertexInputAttributeDescription
|
|
{
|
|
location = 1,
|
|
binding = 0,
|
|
format = VkFormat.R32G32B32Sfloat,
|
|
offset = 12,
|
|
};
|
|
attributes[2] = new VkVertexInputAttributeDescription
|
|
{
|
|
location = 2,
|
|
binding = 0,
|
|
format = VkFormat.R32G32B32Sfloat,
|
|
offset = 24,
|
|
};
|
|
|
|
var vertexInputState = new VkPipelineVertexInputStateCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineVertexInputStateCreateInfo,
|
|
vertexBindingDescriptionCount = 1,
|
|
pVertexBindingDescriptions = bindings,
|
|
vertexAttributeDescriptionCount = 3,
|
|
pVertexAttributeDescriptions = attributes,
|
|
};
|
|
|
|
var inputAssemblyState = new VkPipelineInputAssemblyStateCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineInputAssemblyStateCreateInfo,
|
|
topology = VkPrimitiveTopology.TriangleList,
|
|
primitiveRestartEnable = VkBool32.False,
|
|
};
|
|
|
|
var viewportState = new VkPipelineViewportStateCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineViewportStateCreateInfo,
|
|
viewportCount = 1,
|
|
pViewports = null,
|
|
scissorCount = 1,
|
|
pScissors = null,
|
|
};
|
|
|
|
var rasterizationState = new VkPipelineRasterizationStateCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineRasterizationStateCreateInfo,
|
|
depthClampEnable = VkBool32.False,
|
|
rasterizerDiscardEnable = VkBool32.False,
|
|
polygonMode = VkPolygonMode.Fill,
|
|
cullMode = VkCullModeFlags.None,
|
|
frontFace = VkFrontFace.CounterClockwise,
|
|
depthBiasEnable = VkBool32.False,
|
|
lineWidth = 1.0f,
|
|
};
|
|
|
|
var multisampleState = new VkPipelineMultisampleStateCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineMultisampleStateCreateInfo,
|
|
rasterizationSamples = VkSampleCountFlags.Count1,
|
|
sampleShadingEnable = VkBool32.False,
|
|
};
|
|
|
|
var blendAttachment = new VkPipelineColorBlendAttachmentState
|
|
{
|
|
blendEnable = VkBool32.False,
|
|
colorWriteMask = VkColorComponentFlags.R | VkColorComponentFlags.G | VkColorComponentFlags.B | VkColorComponentFlags.A,
|
|
};
|
|
|
|
var colorBlendState = new VkPipelineColorBlendStateCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineColorBlendStateCreateInfo,
|
|
logicOpEnable = VkBool32.False,
|
|
attachmentCount = 1,
|
|
pAttachments = &blendAttachment,
|
|
};
|
|
|
|
var dynamicStates = stackalloc VkDynamicState[2];
|
|
dynamicStates[0] = VkDynamicState.Viewport;
|
|
dynamicStates[1] = VkDynamicState.Scissor;
|
|
|
|
var dynamicState = new VkPipelineDynamicStateCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineDynamicStateCreateInfo,
|
|
dynamicStateCount = 2,
|
|
pDynamicStates = dynamicStates,
|
|
};
|
|
|
|
var pushConstantRange = new VkPushConstantRange
|
|
{
|
|
stageFlags = VkShaderStageFlags.Vertex,
|
|
offset = 0,
|
|
size = 4,
|
|
};
|
|
|
|
var descLayout = DescriptorSetLayout;
|
|
var layoutInfo = new VkPipelineLayoutCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineLayoutCreateInfo,
|
|
setLayoutCount = 1,
|
|
pSetLayouts = &descLayout,
|
|
pushConstantRangeCount = 1,
|
|
pPushConstantRanges = (nint)(&pushConstantRange),
|
|
};
|
|
|
|
fixed (VkPipelineLayout* layoutPtr = &PipelineLayout)
|
|
{
|
|
var result = Vk.vkCreatePipelineLayout(_device, &layoutInfo, 0, layoutPtr);
|
|
if (result != VkResult.Success)
|
|
throw new InvalidOperationException($"vkCreatePipelineLayout failed: {result}");
|
|
}
|
|
|
|
var renderingInfo = new VkPipelineRenderingCreateInfo
|
|
{
|
|
sType = VkStructureType.PipelineRenderingCreateInfo,
|
|
colorAttachmentCount = 1,
|
|
pColorAttachmentFormats = &colorFormat,
|
|
};
|
|
|
|
var pipelineInfo = new VkGraphicsPipelineCreateInfo
|
|
{
|
|
sType = VkStructureType.GraphicsPipelineCreateInfo,
|
|
pNext = (nint)(&renderingInfo),
|
|
stageCount = 2,
|
|
pStages = stages,
|
|
pVertexInputState = &vertexInputState,
|
|
pInputAssemblyState = &inputAssemblyState,
|
|
pViewportState = &viewportState,
|
|
pRasterizationState = &rasterizationState,
|
|
pMultisampleState = &multisampleState,
|
|
pColorBlendState = &colorBlendState,
|
|
pDynamicState = &dynamicState,
|
|
layout = PipelineLayout,
|
|
renderPass = new VkRenderPass { Handle = 0 },
|
|
subpass = 0,
|
|
};
|
|
|
|
fixed (VkPipeline* pipePtr = &Pipeline)
|
|
{
|
|
var result = Vk.vkCreateGraphicsPipelines(_device, 0, 1, &pipelineInfo, 0, pipePtr);
|
|
if (result != VkResult.Success)
|
|
throw new InvalidOperationException($"vkCreateGraphicsPipelines failed: {result}");
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("[Vulkan] Graphics pipeline created (dynamic rendering)");
|
|
}
|
|
|
|
private void CreateDescriptorSetLayout()
|
|
{
|
|
var binding = new VkDescriptorSetLayoutBinding
|
|
{
|
|
binding = 0,
|
|
descriptorType = VkDescriptorType.UniformBuffer,
|
|
descriptorCount = 1,
|
|
stageFlags = VkShaderStageFlags.Vertex,
|
|
};
|
|
|
|
var info = new VkDescriptorSetLayoutCreateInfo
|
|
{
|
|
sType = VkStructureType.DescriptorSetLayoutCreateInfo,
|
|
bindingCount = 1,
|
|
pBindings = &binding,
|
|
};
|
|
|
|
var descLayout = VkDescriptorSetLayout.Null;
|
|
var result = Vk.vkCreateDescriptorSetLayout(_device, &info, 0, &descLayout);
|
|
if (result != VkResult.Success)
|
|
throw new InvalidOperationException($"vkCreateDescriptorSetLayout failed: {result}");
|
|
DescriptorSetLayout = descLayout;
|
|
}
|
|
|
|
private VkShaderModule CreateShaderModule(byte[] spv)
|
|
{
|
|
fixed (byte* pCode = spv)
|
|
{
|
|
var info = new VkShaderModuleCreateInfo
|
|
{
|
|
sType = VkStructureType.ShaderModuleCreateInfo,
|
|
codeSize = (nuint)spv.Length,
|
|
pCode = (uint*)pCode,
|
|
};
|
|
|
|
var module = VkShaderModule.Null;
|
|
var result = Vk.vkCreateShaderModule(_device, &info, 0, &module);
|
|
if (result != VkResult.Success)
|
|
throw new InvalidOperationException($"vkCreateShaderModule failed: {result}");
|
|
return module;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_disposed) return;
|
|
_disposed = true;
|
|
|
|
if (Pipeline.Handle != 0) Vk.vkDestroyPipeline(_device, Pipeline, 0);
|
|
if (PipelineLayout.Handle != 0) Vk.vkDestroyPipelineLayout(_device, PipelineLayout, 0);
|
|
if (DescriptorSetLayout.Handle != 0) Vk.vkDestroyDescriptorSetLayout(_device, DescriptorSetLayout, 0);
|
|
if (FragModule.Handle != 0) Vk.vkDestroyShaderModule(_device, FragModule, 0);
|
|
if (VertModule.Handle != 0) Vk.vkDestroyShaderModule(_device, VertModule, 0);
|
|
}
|
|
}
|