feat: push constants + UBO descriptor sets — rotating triangle with VP matrix

- 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
This commit is contained in:
emil28092005
2026-06-18 01:59:41 +03:00
parent 2e0970e769
commit 4f3810010a
9 changed files with 328 additions and 16 deletions
+40 -2
View File
@@ -9,6 +9,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable
public VkPipeline Pipeline;
public VkShaderModule VertModule;
public VkShaderModule FragModule;
public VkDescriptorSetLayout DescriptorSetLayout;
private readonly VkDevice _device;
private bool _disposed;
@@ -19,6 +20,8 @@ internal sealed unsafe class VulkanPipeline : IDisposable
VertModule = CreateShaderModule(vertSpv);
FragModule = CreateShaderModule(fragSpv);
CreateDescriptorSetLayout();
fixed (byte* pName = "main\0"u8)
{
var stages = stackalloc VkPipelineShaderStageCreateInfo[2];
@@ -137,11 +140,21 @@ internal sealed unsafe class VulkanPipeline : IDisposable
pDynamicStates = dynamicStates,
};
var pushConstantRange = new VkPushConstantRange
{
stageFlags = VkShaderStageFlags.Vertex,
offset = 0,
size = 4,
};
var descLayout = DescriptorSetLayout;
var layoutInfo = new VkPipelineLayoutCreateInfo
{
sType = VkStructureType.PipelineLayoutCreateInfo,
setLayoutCount = 0,
pushConstantRangeCount = 0,
setLayoutCount = 1,
pSetLayouts = &descLayout,
pushConstantRangeCount = 1,
pPushConstantRanges = (nint)(&pushConstantRange),
};
fixed (VkPipelineLayout* layoutPtr = &PipelineLayout)
@@ -187,6 +200,30 @@ internal sealed unsafe class VulkanPipeline : IDisposable
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)
@@ -213,6 +250,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable
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);
}