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:
@@ -6,7 +6,22 @@ layout(location = 2) in vec3 inNormal;
|
|||||||
|
|
||||||
layout(location = 0) out vec3 fragColor;
|
layout(location = 0) out vec3 fragColor;
|
||||||
|
|
||||||
|
layout(row_major, set = 0, binding = 0) uniform CameraUBO {
|
||||||
|
mat4 vp;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(push_constant) uniform PC {
|
||||||
|
float angle;
|
||||||
|
} pc;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(inPosition, 1.0);
|
float c = cos(pc.angle);
|
||||||
|
float s = sin(pc.angle);
|
||||||
|
vec3 rotated = vec3(
|
||||||
|
inPosition.x * c - inPosition.y * s,
|
||||||
|
inPosition.x * s + inPosition.y * c,
|
||||||
|
inPosition.z
|
||||||
|
);
|
||||||
|
gl_Position = vp * vec4(rotated, 1.0);
|
||||||
fragColor = inColor;
|
fragColor = inColor;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -62,6 +62,14 @@ internal static unsafe class Vk
|
|||||||
public delegate void VkCmdEndRendering(VkCommandBuffer commandBuffer);
|
public delegate void VkCmdEndRendering(VkCommandBuffer commandBuffer);
|
||||||
public delegate void VkCmdPipelineBarrier2(VkCommandBuffer commandBuffer, VkDependencyInfo* pDependencyInfo);
|
public delegate void VkCmdPipelineBarrier2(VkCommandBuffer commandBuffer, VkDependencyInfo* pDependencyInfo);
|
||||||
public delegate void VkCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint regionCount, VkBufferCopy* pRegions);
|
public delegate void VkCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint regionCount, VkBufferCopy* pRegions);
|
||||||
|
public delegate void VkCmdPushConstants(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint offset, uint size, void* pValues);
|
||||||
|
public delegate void VkCmdBindDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint firstSet, uint descriptorSetCount, VkDescriptorSet* pDescriptorSets, uint dynamicOffsetCount, uint* pDynamicOffsets);
|
||||||
|
public delegate VkResult VkCreateDescriptorSetLayout(VkDevice device, VkDescriptorSetLayoutCreateInfo* pCreateInfo, nint pAllocator, VkDescriptorSetLayout* pSetLayout);
|
||||||
|
public delegate void VkDestroyDescriptorSetLayout(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, nint pAllocator);
|
||||||
|
public delegate VkResult VkCreateDescriptorPool(VkDevice device, VkDescriptorPoolCreateInfo* pCreateInfo, nint pAllocator, VkDescriptorPool* pDescriptorPool);
|
||||||
|
public delegate void VkDestroyDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, nint pAllocator);
|
||||||
|
public delegate VkResult VkAllocateDescriptorSets(VkDevice device, VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets);
|
||||||
|
public delegate void VkUpdateDescriptorSets(VkDevice device, uint descriptorWriteCount, VkWriteDescriptorSet* pDescriptorWrites, uint descriptorCopyCount, nint pDescriptorCopies);
|
||||||
public delegate VkResult VkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, ulong timeout, VkSemaphore semaphore, VkFence fence, uint* pImageIndex);
|
public delegate VkResult VkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, ulong timeout, VkSemaphore semaphore, VkFence fence, uint* pImageIndex);
|
||||||
public delegate VkResult VkQueueSubmit2(VkQueue queue, uint submitCount, VkSubmitInfo2* pSubmits, VkFence fence);
|
public delegate VkResult VkQueueSubmit2(VkQueue queue, uint submitCount, VkSubmitInfo2* pSubmits, VkFence fence);
|
||||||
public delegate VkResult VkQueuePresentKHR(VkQueue queue, VkPresentInfoKHR* pPresentInfo);
|
public delegate VkResult VkQueuePresentKHR(VkQueue queue, VkPresentInfoKHR* pPresentInfo);
|
||||||
@@ -129,6 +137,14 @@ internal static unsafe class Vk
|
|||||||
public static VkCmdEndRendering vkCmdEndRendering;
|
public static VkCmdEndRendering vkCmdEndRendering;
|
||||||
public static VkCmdPipelineBarrier2 vkCmdPipelineBarrier2;
|
public static VkCmdPipelineBarrier2 vkCmdPipelineBarrier2;
|
||||||
public static VkCmdCopyBuffer vkCmdCopyBuffer;
|
public static VkCmdCopyBuffer vkCmdCopyBuffer;
|
||||||
|
public static VkCmdPushConstants vkCmdPushConstants;
|
||||||
|
public static VkCmdBindDescriptorSets vkCmdBindDescriptorSets;
|
||||||
|
public static VkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
|
||||||
|
public static VkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
|
||||||
|
public static VkCreateDescriptorPool vkCreateDescriptorPool;
|
||||||
|
public static VkDestroyDescriptorPool vkDestroyDescriptorPool;
|
||||||
|
public static VkAllocateDescriptorSets vkAllocateDescriptorSets;
|
||||||
|
public static VkUpdateDescriptorSets vkUpdateDescriptorSets;
|
||||||
public static VkAcquireNextImageKHR vkAcquireNextImageKHR;
|
public static VkAcquireNextImageKHR vkAcquireNextImageKHR;
|
||||||
public static VkQueueSubmit2 vkQueueSubmit2;
|
public static VkQueueSubmit2 vkQueueSubmit2;
|
||||||
public static VkQueuePresentKHR vkQueuePresentKHR;
|
public static VkQueuePresentKHR vkQueuePresentKHR;
|
||||||
@@ -203,6 +219,14 @@ internal static unsafe class Vk
|
|||||||
vkCmdEndRendering = LoadDev<VkCmdEndRendering>(p, "vkCmdEndRendering");
|
vkCmdEndRendering = LoadDev<VkCmdEndRendering>(p, "vkCmdEndRendering");
|
||||||
vkCmdPipelineBarrier2 = LoadDev<VkCmdPipelineBarrier2>(p, "vkCmdPipelineBarrier2");
|
vkCmdPipelineBarrier2 = LoadDev<VkCmdPipelineBarrier2>(p, "vkCmdPipelineBarrier2");
|
||||||
vkCmdCopyBuffer = LoadDev<VkCmdCopyBuffer>(p, "vkCmdCopyBuffer");
|
vkCmdCopyBuffer = LoadDev<VkCmdCopyBuffer>(p, "vkCmdCopyBuffer");
|
||||||
|
vkCmdPushConstants = LoadDev<VkCmdPushConstants>(p, "vkCmdPushConstants");
|
||||||
|
vkCmdBindDescriptorSets = LoadDev<VkCmdBindDescriptorSets>(p, "vkCmdBindDescriptorSets");
|
||||||
|
vkCreateDescriptorSetLayout = LoadDev<VkCreateDescriptorSetLayout>(p, "vkCreateDescriptorSetLayout");
|
||||||
|
vkDestroyDescriptorSetLayout = LoadDev<VkDestroyDescriptorSetLayout>(p, "vkDestroyDescriptorSetLayout");
|
||||||
|
vkCreateDescriptorPool = LoadDev<VkCreateDescriptorPool>(p, "vkCreateDescriptorPool");
|
||||||
|
vkDestroyDescriptorPool = LoadDev<VkDestroyDescriptorPool>(p, "vkDestroyDescriptorPool");
|
||||||
|
vkAllocateDescriptorSets = LoadDev<VkAllocateDescriptorSets>(p, "vkAllocateDescriptorSets");
|
||||||
|
vkUpdateDescriptorSets = LoadDev<VkUpdateDescriptorSets>(p, "vkUpdateDescriptorSets");
|
||||||
vkAcquireNextImageKHR = LoadDev<VkAcquireNextImageKHR>(p, "vkAcquireNextImageKHR");
|
vkAcquireNextImageKHR = LoadDev<VkAcquireNextImageKHR>(p, "vkAcquireNextImageKHR");
|
||||||
vkQueueSubmit2 = LoadDev<VkQueueSubmit2>(p, "vkQueueSubmit2");
|
vkQueueSubmit2 = LoadDev<VkQueueSubmit2>(p, "vkQueueSubmit2");
|
||||||
vkQueuePresentKHR = LoadDev<VkQueuePresentKHR>(p, "vkQueuePresentKHR");
|
vkQueuePresentKHR = LoadDev<VkQueuePresentKHR>(p, "vkQueuePresentKHR");
|
||||||
|
|||||||
@@ -241,12 +241,13 @@ internal sealed unsafe class VulkanContext : IDisposable
|
|||||||
var nameLen = Array.IndexOf(nameBytes, (byte)0);
|
var nameLen = Array.IndexOf(nameBytes, (byte)0);
|
||||||
if (nameLen < 0) nameLen = 256;
|
if (nameLen < 0) nameLen = 256;
|
||||||
var devType = (VkPhysicalDeviceType)Marshal.ReadInt32((nint)propsBytes, 16);
|
var devType = (VkPhysicalDeviceType)Marshal.ReadInt32((nint)propsBytes, 16);
|
||||||
Console.WriteLine($"[Vulkan] GPU {i}: {System.Text.Encoding.UTF8.GetString(nameBytes, 0, nameLen)} (type={devType})");
|
var gpuName = System.Text.Encoding.UTF8.GetString(nameBytes, 0, nameLen);
|
||||||
|
|
||||||
if (best.Handle == 0 || (devType == VkPhysicalDeviceType.DiscreteGpu && bestType != VkPhysicalDeviceType.DiscreteGpu))
|
if (best.Handle == 0 || (devType == VkPhysicalDeviceType.DiscreteGpu && bestType != VkPhysicalDeviceType.DiscreteGpu))
|
||||||
{
|
{
|
||||||
best = devices[(int)i];
|
best = devices[(int)i];
|
||||||
bestType = devType;
|
bestType = devType;
|
||||||
|
Console.WriteLine($"[Vulkan] Selected GPU: {gpuName} (type={devType})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +374,6 @@ internal sealed unsafe class VulkanContext : IDisposable
|
|||||||
SurfaceColorSpace = formats[0].colorSpace;
|
SurfaceColorSpace = formats[0].colorSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"[Vulkan] Surface format: {SurfaceFormat}, color space: {SurfaceColorSpace}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint FindMemoryType(uint memoryTypeBits, VkMemoryPropertyFlags desiredFlags)
|
public uint FindMemoryType(uint memoryTypeBits, VkMemoryPropertyFlags desiredFlags)
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ public enum VkStructureType : int
|
|||||||
MemoryAllocateInfo = 5,
|
MemoryAllocateInfo = 5,
|
||||||
BufferCreateInfo = 12,
|
BufferCreateInfo = 12,
|
||||||
ShaderModuleCreateInfo = 16,
|
ShaderModuleCreateInfo = 16,
|
||||||
|
DescriptorSetLayoutCreateInfo = 32,
|
||||||
|
DescriptorPoolCreateInfo = 33,
|
||||||
|
DescriptorSetAllocateInfo = 34,
|
||||||
|
WriteDescriptorSet = 35,
|
||||||
PipelineShaderStageCreateInfo = 18,
|
PipelineShaderStageCreateInfo = 18,
|
||||||
PipelineVertexInputStateCreateInfo = 19,
|
PipelineVertexInputStateCreateInfo = 19,
|
||||||
PipelineInputAssemblyStateCreateInfo = 20,
|
PipelineInputAssemblyStateCreateInfo = 20,
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Engine.Graphics.Vulkan;
|
namespace Engine.Graphics.Vulkan;
|
||||||
|
|
||||||
internal sealed unsafe class VulkanFrameResources : IDisposable
|
internal sealed unsafe class VulkanFrameResources : IDisposable
|
||||||
{
|
{
|
||||||
public const int MaxFramesInFlight = 2;
|
public const int MaxFramesInFlight = 2;
|
||||||
|
public const ulong UboSize = 64;
|
||||||
|
|
||||||
public VkCommandPool CommandPool;
|
public VkCommandPool CommandPool;
|
||||||
public VkCommandBuffer[] CommandBuffers = new VkCommandBuffer[MaxFramesInFlight];
|
public VkCommandBuffer[] CommandBuffers = new VkCommandBuffer[MaxFramesInFlight];
|
||||||
@@ -10,10 +13,17 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
|||||||
public VkSemaphore[] AcquireSemaphores = new VkSemaphore[MaxFramesInFlight];
|
public VkSemaphore[] AcquireSemaphores = new VkSemaphore[MaxFramesInFlight];
|
||||||
public VkSemaphore[] SubmitSemaphores = Array.Empty<VkSemaphore>();
|
public VkSemaphore[] SubmitSemaphores = Array.Empty<VkSemaphore>();
|
||||||
|
|
||||||
|
public VkBuffer[] UboBuffers = new VkBuffer[MaxFramesInFlight];
|
||||||
|
public VkDeviceMemory[] UboMemories = new VkDeviceMemory[MaxFramesInFlight];
|
||||||
|
public VkDescriptorSet[] DescriptorSets = new VkDescriptorSet[MaxFramesInFlight];
|
||||||
|
|
||||||
|
public VkDescriptorPool DescriptorPool;
|
||||||
|
|
||||||
private readonly VkDevice _device;
|
private readonly VkDevice _device;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public VulkanFrameResources(VkDevice device, uint queueFamilyIndex, uint swapchainImageCount)
|
public VulkanFrameResources(VkDevice device, uint queueFamilyIndex, uint swapchainImageCount,
|
||||||
|
VulkanContext ctx, VkDescriptorSetLayout descriptorSetLayout)
|
||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
|
|
||||||
@@ -24,12 +34,11 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
|||||||
queueFamilyIndex = queueFamilyIndex,
|
queueFamilyIndex = queueFamilyIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
fixed (VkCommandPool* poolPtr = &CommandPool)
|
var cmdPool = VkCommandPool.Null;
|
||||||
{
|
var result = Vk.vkCreateCommandPool(_device, &poolInfo, 0, &cmdPool);
|
||||||
var result = Vk.vkCreateCommandPool(_device, &poolInfo, 0, poolPtr);
|
if (result != VkResult.Success)
|
||||||
if (result != VkResult.Success)
|
throw new InvalidOperationException($"vkCreateCommandPool failed: {result}");
|
||||||
throw new InvalidOperationException($"vkCreateCommandPool failed: {result}");
|
CommandPool = cmdPool;
|
||||||
}
|
|
||||||
|
|
||||||
var allocInfo = new VkCommandBufferAllocateInfo
|
var allocInfo = new VkCommandBufferAllocateInfo
|
||||||
{
|
{
|
||||||
@@ -41,7 +50,7 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
|||||||
|
|
||||||
fixed (VkCommandBuffer* cmdPtr = CommandBuffers)
|
fixed (VkCommandBuffer* cmdPtr = CommandBuffers)
|
||||||
{
|
{
|
||||||
var result = Vk.vkAllocateCommandBuffers(_device, &allocInfo, cmdPtr);
|
result = Vk.vkAllocateCommandBuffers(_device, &allocInfo, cmdPtr);
|
||||||
if (result != VkResult.Success)
|
if (result != VkResult.Success)
|
||||||
throw new InvalidOperationException($"vkAllocateCommandBuffers failed: {result}");
|
throw new InvalidOperationException($"vkAllocateCommandBuffers failed: {result}");
|
||||||
}
|
}
|
||||||
@@ -60,7 +69,7 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
|||||||
for (int i = 0; i < MaxFramesInFlight; i++)
|
for (int i = 0; i < MaxFramesInFlight; i++)
|
||||||
{
|
{
|
||||||
var fence = VkFence.Null;
|
var fence = VkFence.Null;
|
||||||
var result = Vk.vkCreateFence(_device, &fenceInfo, 0, &fence);
|
result = Vk.vkCreateFence(_device, &fenceInfo, 0, &fence);
|
||||||
if (result != VkResult.Success)
|
if (result != VkResult.Success)
|
||||||
throw new InvalidOperationException($"vkCreateFence failed: {result}");
|
throw new InvalidOperationException($"vkCreateFence failed: {result}");
|
||||||
FrameFences[i] = fence;
|
FrameFences[i] = fence;
|
||||||
@@ -76,15 +85,133 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
|||||||
for (int i = 0; i < swapchainImageCount; i++)
|
for (int i = 0; i < swapchainImageCount; i++)
|
||||||
{
|
{
|
||||||
var sem = VkSemaphore.Null;
|
var sem = VkSemaphore.Null;
|
||||||
var result = Vk.vkCreateSemaphore(_device, &semInfo, 0, &sem);
|
result = Vk.vkCreateSemaphore(_device, &semInfo, 0, &sem);
|
||||||
if (result != VkResult.Success)
|
if (result != VkResult.Success)
|
||||||
throw new InvalidOperationException($"vkCreateSemaphore (submit) failed: {result}");
|
throw new InvalidOperationException($"vkCreateSemaphore (submit) failed: {result}");
|
||||||
SubmitSemaphores[i] = sem;
|
SubmitSemaphores[i] = sem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CreateUniformBuffers(ctx);
|
||||||
|
CreateDescriptorPoolAndSets(descriptorSetLayout);
|
||||||
|
|
||||||
Console.WriteLine($"[Vulkan] Frame resources: {MaxFramesInFlight} frames in flight, {swapchainImageCount} submit semaphores");
|
Console.WriteLine($"[Vulkan] Frame resources: {MaxFramesInFlight} frames in flight, {swapchainImageCount} submit semaphores");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CreateUniformBuffers(VulkanContext ctx)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MaxFramesInFlight; i++)
|
||||||
|
{
|
||||||
|
var info = new VkBufferCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.BufferCreateInfo,
|
||||||
|
size = UboSize,
|
||||||
|
usage = VkBufferUsageFlags.UniformBuffer,
|
||||||
|
sharingMode = VkSharingMode.Exclusive,
|
||||||
|
};
|
||||||
|
|
||||||
|
var buf = VkBuffer.Null;
|
||||||
|
var result = Vk.vkCreateBuffer(_device, &info, 0, &buf);
|
||||||
|
if (result != VkResult.Success)
|
||||||
|
throw new InvalidOperationException($"vkCreateBuffer (UBO) failed: {result}");
|
||||||
|
UboBuffers[i] = buf;
|
||||||
|
|
||||||
|
var reqs = new VkMemoryRequirements();
|
||||||
|
Vk.vkGetBufferMemoryRequirements(_device, buf, &reqs);
|
||||||
|
|
||||||
|
var memTypeIndex = ctx.FindMemoryType(reqs.memoryTypeBits,
|
||||||
|
VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent);
|
||||||
|
|
||||||
|
var allocInfo = new VkMemoryAllocateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.MemoryAllocateInfo,
|
||||||
|
allocationSize = reqs.size,
|
||||||
|
memoryTypeIndex = memTypeIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
var mem = VkDeviceMemory.Null;
|
||||||
|
result = Vk.vkAllocateMemory(_device, &allocInfo, 0, &mem);
|
||||||
|
if (result != VkResult.Success)
|
||||||
|
throw new InvalidOperationException($"vkAllocateMemory (UBO) failed: {result}");
|
||||||
|
UboMemories[i] = mem;
|
||||||
|
|
||||||
|
Vk.vkBindBufferMemory(_device, buf, mem, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateDescriptorPoolAndSets(VkDescriptorSetLayout layout)
|
||||||
|
{
|
||||||
|
var poolSize = new VkDescriptorPoolSize
|
||||||
|
{
|
||||||
|
type = VkDescriptorType.UniformBuffer,
|
||||||
|
descriptorCount = MaxFramesInFlight,
|
||||||
|
};
|
||||||
|
|
||||||
|
var poolInfo = new VkDescriptorPoolCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.DescriptorPoolCreateInfo,
|
||||||
|
flags = VkDescriptorPoolCreateFlags.FreeDescriptorSet,
|
||||||
|
maxSets = MaxFramesInFlight,
|
||||||
|
poolSizeCount = 1,
|
||||||
|
pPoolSizes = &poolSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
var descPool = VkDescriptorPool.Null;
|
||||||
|
var result = Vk.vkCreateDescriptorPool(_device, &poolInfo, 0, &descPool);
|
||||||
|
if (result != VkResult.Success)
|
||||||
|
throw new InvalidOperationException($"vkCreateDescriptorPool failed: {result}");
|
||||||
|
DescriptorPool = descPool;
|
||||||
|
|
||||||
|
var layouts = stackalloc VkDescriptorSetLayout[MaxFramesInFlight];
|
||||||
|
for (int i = 0; i < MaxFramesInFlight; i++)
|
||||||
|
layouts[i] = layout;
|
||||||
|
|
||||||
|
var allocInfo = new VkDescriptorSetAllocateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.DescriptorSetAllocateInfo,
|
||||||
|
descriptorPool = DescriptorPool,
|
||||||
|
descriptorSetCount = MaxFramesInFlight,
|
||||||
|
pSetLayouts = layouts,
|
||||||
|
};
|
||||||
|
|
||||||
|
fixed (VkDescriptorSet* setPtr = DescriptorSets)
|
||||||
|
{
|
||||||
|
result = Vk.vkAllocateDescriptorSets(_device, &allocInfo, setPtr);
|
||||||
|
if (result != VkResult.Success)
|
||||||
|
throw new InvalidOperationException($"vkAllocateDescriptorSets failed: {result}");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MaxFramesInFlight; i++)
|
||||||
|
{
|
||||||
|
var bufferInfo = new VkDescriptorBufferInfo
|
||||||
|
{
|
||||||
|
buffer = UboBuffers[i],
|
||||||
|
offset = 0,
|
||||||
|
range = UboSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
var write = new VkWriteDescriptorSet
|
||||||
|
{
|
||||||
|
sType = VkStructureType.WriteDescriptorSet,
|
||||||
|
dstSet = DescriptorSets[i],
|
||||||
|
dstBinding = 0,
|
||||||
|
dstArrayElement = 0,
|
||||||
|
descriptorCount = 1,
|
||||||
|
descriptorType = VkDescriptorType.UniformBuffer,
|
||||||
|
pBufferInfo = &bufferInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
Vk.vkUpdateDescriptorSets(_device, 1, &write, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateUbo(int frameIndex, void* data, ulong size)
|
||||||
|
{
|
||||||
|
void* pData = null;
|
||||||
|
Vk.vkMapMemory(_device, UboMemories[frameIndex], 0, size, 0, &pData);
|
||||||
|
System.Buffer.MemoryCopy(data, pData, (long)size, (long)size);
|
||||||
|
Vk.vkUnmapMemory(_device, UboMemories[frameIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
public void WaitFrame(int frameIndex)
|
public void WaitFrame(int frameIndex)
|
||||||
{
|
{
|
||||||
fixed (VkFence* fencePtr = &FrameFences[frameIndex])
|
fixed (VkFence* fencePtr = &FrameFences[frameIndex])
|
||||||
@@ -105,6 +232,8 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
|||||||
{
|
{
|
||||||
if (FrameFences[i].Handle != 0) Vk.vkDestroyFence(_device, FrameFences[i], 0);
|
if (FrameFences[i].Handle != 0) Vk.vkDestroyFence(_device, FrameFences[i], 0);
|
||||||
if (AcquireSemaphores[i].Handle != 0) Vk.vkDestroySemaphore(_device, AcquireSemaphores[i], 0);
|
if (AcquireSemaphores[i].Handle != 0) Vk.vkDestroySemaphore(_device, AcquireSemaphores[i], 0);
|
||||||
|
if (UboBuffers[i].Handle != 0) Vk.vkDestroyBuffer(_device, UboBuffers[i], 0);
|
||||||
|
if (UboMemories[i].Handle != 0) Vk.vkFreeMemory(_device, UboMemories[i], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < SubmitSemaphores.Length; i++)
|
for (int i = 0; i < SubmitSemaphores.Length; i++)
|
||||||
@@ -112,6 +241,7 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
|||||||
if (SubmitSemaphores[i].Handle != 0) Vk.vkDestroySemaphore(_device, SubmitSemaphores[i], 0);
|
if (SubmitSemaphores[i].Handle != 0) Vk.vkDestroySemaphore(_device, SubmitSemaphores[i], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DescriptorPool.Handle != 0) Vk.vkDestroyDescriptorPool(_device, DescriptorPool, 0);
|
||||||
if (CommandPool.Handle != 0) Vk.vkDestroyCommandPool(_device, CommandPool, 0);
|
if (CommandPool.Handle != 0) Vk.vkDestroyCommandPool(_device, CommandPool, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
public VkPipeline Pipeline;
|
public VkPipeline Pipeline;
|
||||||
public VkShaderModule VertModule;
|
public VkShaderModule VertModule;
|
||||||
public VkShaderModule FragModule;
|
public VkShaderModule FragModule;
|
||||||
|
public VkDescriptorSetLayout DescriptorSetLayout;
|
||||||
|
|
||||||
private readonly VkDevice _device;
|
private readonly VkDevice _device;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
@@ -19,6 +20,8 @@ internal sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
VertModule = CreateShaderModule(vertSpv);
|
VertModule = CreateShaderModule(vertSpv);
|
||||||
FragModule = CreateShaderModule(fragSpv);
|
FragModule = CreateShaderModule(fragSpv);
|
||||||
|
|
||||||
|
CreateDescriptorSetLayout();
|
||||||
|
|
||||||
fixed (byte* pName = "main\0"u8)
|
fixed (byte* pName = "main\0"u8)
|
||||||
{
|
{
|
||||||
var stages = stackalloc VkPipelineShaderStageCreateInfo[2];
|
var stages = stackalloc VkPipelineShaderStageCreateInfo[2];
|
||||||
@@ -137,11 +140,21 @@ internal sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
pDynamicStates = dynamicStates,
|
pDynamicStates = dynamicStates,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var pushConstantRange = new VkPushConstantRange
|
||||||
|
{
|
||||||
|
stageFlags = VkShaderStageFlags.Vertex,
|
||||||
|
offset = 0,
|
||||||
|
size = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
var descLayout = DescriptorSetLayout;
|
||||||
var layoutInfo = new VkPipelineLayoutCreateInfo
|
var layoutInfo = new VkPipelineLayoutCreateInfo
|
||||||
{
|
{
|
||||||
sType = VkStructureType.PipelineLayoutCreateInfo,
|
sType = VkStructureType.PipelineLayoutCreateInfo,
|
||||||
setLayoutCount = 0,
|
setLayoutCount = 1,
|
||||||
pushConstantRangeCount = 0,
|
pSetLayouts = &descLayout,
|
||||||
|
pushConstantRangeCount = 1,
|
||||||
|
pPushConstantRanges = (nint)(&pushConstantRange),
|
||||||
};
|
};
|
||||||
|
|
||||||
fixed (VkPipelineLayout* layoutPtr = &PipelineLayout)
|
fixed (VkPipelineLayout* layoutPtr = &PipelineLayout)
|
||||||
@@ -187,6 +200,30 @@ internal sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
Console.WriteLine("[Vulkan] Graphics pipeline created (dynamic rendering)");
|
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)
|
private VkShaderModule CreateShaderModule(byte[] spv)
|
||||||
{
|
{
|
||||||
fixed (byte* pCode = 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 (Pipeline.Handle != 0) Vk.vkDestroyPipeline(_device, Pipeline, 0);
|
||||||
if (PipelineLayout.Handle != 0) Vk.vkDestroyPipelineLayout(_device, PipelineLayout, 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 (FragModule.Handle != 0) Vk.vkDestroyShaderModule(_device, FragModule, 0);
|
||||||
if (VertModule.Handle != 0) Vk.vkDestroyShaderModule(_device, VertModule, 0);
|
if (VertModule.Handle != 0) Vk.vkDestroyShaderModule(_device, VertModule, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private bool _screenshotRequested;
|
private bool _screenshotRequested;
|
||||||
private string? _screenshotPath;
|
private string? _screenshotPath;
|
||||||
|
private float _totalTime;
|
||||||
|
|
||||||
public bool IsScreenshotRequested => _screenshotRequested;
|
public bool IsScreenshotRequested => _screenshotRequested;
|
||||||
public IScreenshotProvider ScreenshotProvider => this;
|
public IScreenshotProvider ScreenshotProvider => this;
|
||||||
@@ -32,7 +33,8 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
|
|
||||||
_pipeline = new VulkanPipeline(ctx.Device, swapchain.Format, vertSpv, fragSpv);
|
_pipeline = new VulkanPipeline(ctx.Device, swapchain.Format, vertSpv, fragSpv);
|
||||||
|
|
||||||
_frameResources = new VulkanFrameResources(ctx.Device, ctx.GraphicsQueueFamilyIndex, swapchain.ImageCount);
|
_frameResources = new VulkanFrameResources(ctx.Device, ctx.GraphicsQueueFamilyIndex,
|
||||||
|
swapchain.ImageCount, ctx, _pipeline.DescriptorSetLayout);
|
||||||
|
|
||||||
var vertices = new Vertex[]
|
var vertices = new Vertex[]
|
||||||
{
|
{
|
||||||
@@ -69,6 +71,11 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
if (acquireResult != VkResult.Success)
|
if (acquireResult != VkResult.Success)
|
||||||
throw new InvalidOperationException($"vkAcquireNextImageKHR failed: {acquireResult}");
|
throw new InvalidOperationException($"vkAcquireNextImageKHR failed: {acquireResult}");
|
||||||
|
|
||||||
|
_totalTime += 0.016f;
|
||||||
|
|
||||||
|
var vp = ComputeViewProjection();
|
||||||
|
_frameResources.UpdateUbo(_frameIndex, &vp, VulkanFrameResources.UboSize);
|
||||||
|
|
||||||
var cmd = _frameResources.CommandBuffers[_frameIndex];
|
var cmd = _frameResources.CommandBuffers[_frameIndex];
|
||||||
Vk.vkResetCommandBuffer(cmd, 0);
|
Vk.vkResetCommandBuffer(cmd, 0);
|
||||||
|
|
||||||
@@ -132,10 +139,17 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
};
|
};
|
||||||
Vk.vkCmdSetScissor(cmd, 0, 1, &scissor);
|
Vk.vkCmdSetScissor(cmd, 0, 1, &scissor);
|
||||||
|
|
||||||
|
var descSet = _frameResources.DescriptorSets[_frameIndex];
|
||||||
|
Vk.vkCmdBindDescriptorSets(cmd, VkPipelineBindPoint.Graphics, _pipeline.PipelineLayout,
|
||||||
|
0, 1, &descSet, 0, null);
|
||||||
|
|
||||||
var bufferHandle = _vertexBuffer.Buffer;
|
var bufferHandle = _vertexBuffer.Buffer;
|
||||||
ulong offset = 0;
|
ulong offset = 0;
|
||||||
Vk.vkCmdBindVertexBuffers(cmd, 0, 1, &bufferHandle, &offset);
|
Vk.vkCmdBindVertexBuffers(cmd, 0, 1, &bufferHandle, &offset);
|
||||||
|
|
||||||
|
var angle = _totalTime;
|
||||||
|
Vk.vkCmdPushConstants(cmd, _pipeline.PipelineLayout, VkShaderStageFlags.Vertex, 0, 4, &angle);
|
||||||
|
|
||||||
Vk.vkCmdDraw(cmd, 3, 1, 0, 0);
|
Vk.vkCmdDraw(cmd, 3, 1, 0, 0);
|
||||||
|
|
||||||
Vk.vkCmdEndRendering(cmd);
|
Vk.vkCmdEndRendering(cmd);
|
||||||
@@ -207,6 +221,14 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
_frameIndex = (_frameIndex + 1) % VulkanFrameResources.MaxFramesInFlight;
|
_frameIndex = (_frameIndex + 1) % VulkanFrameResources.MaxFramesInFlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Matrix4x4 ComputeViewProjection()
|
||||||
|
{
|
||||||
|
var aspect = (float)_swapchain.Extent.Width / (float)_swapchain.Extent.Height;
|
||||||
|
var proj = Matrix4x4.CreatePerspectiveFieldOfView(MathF.PI / 4f, aspect, 0.1f, 100f);
|
||||||
|
var view = Matrix4x4.CreateLookAt(new Vector3(0, 0, -2), Vector3.Zero, Vector3.UnitY);
|
||||||
|
return view * proj;
|
||||||
|
}
|
||||||
|
|
||||||
private static void TransitionImageLayout(VkCommandBuffer cmd, VkImage image,
|
private static void TransitionImageLayout(VkCommandBuffer cmd, VkImage image,
|
||||||
VkImageLayout oldLayout, VkImageLayout newLayout,
|
VkImageLayout oldLayout, VkImageLayout newLayout,
|
||||||
ulong srcStage, ulong srcAccess,
|
ulong srcStage, ulong srcAccess,
|
||||||
|
|||||||
@@ -907,3 +907,82 @@ public unsafe struct VkDebugUtilsMessengerCallbackDataEXT
|
|||||||
public uint objectCount;
|
public uint objectCount;
|
||||||
public nint pObjects;
|
public nint pObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkPushConstantRange
|
||||||
|
{
|
||||||
|
public VkShaderStageFlags stageFlags;
|
||||||
|
public uint offset;
|
||||||
|
public uint size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkDescriptorSetLayoutBinding
|
||||||
|
{
|
||||||
|
public uint binding;
|
||||||
|
public VkDescriptorType descriptorType;
|
||||||
|
public uint descriptorCount;
|
||||||
|
public VkShaderStageFlags stageFlags;
|
||||||
|
public nint pImmutableSamplers;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkDescriptorSetLayoutCreateInfo
|
||||||
|
{
|
||||||
|
public VkStructureType sType;
|
||||||
|
public nint pNext;
|
||||||
|
public uint flags;
|
||||||
|
public uint bindingCount;
|
||||||
|
public VkDescriptorSetLayoutBinding* pBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkDescriptorPoolSize
|
||||||
|
{
|
||||||
|
public VkDescriptorType type;
|
||||||
|
public uint descriptorCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkDescriptorPoolCreateInfo
|
||||||
|
{
|
||||||
|
public VkStructureType sType;
|
||||||
|
public nint pNext;
|
||||||
|
public VkDescriptorPoolCreateFlags flags;
|
||||||
|
public uint maxSets;
|
||||||
|
public uint poolSizeCount;
|
||||||
|
public VkDescriptorPoolSize* pPoolSizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkDescriptorSetAllocateInfo
|
||||||
|
{
|
||||||
|
public VkStructureType sType;
|
||||||
|
public nint pNext;
|
||||||
|
public VkDescriptorPool descriptorPool;
|
||||||
|
public uint descriptorSetCount;
|
||||||
|
public VkDescriptorSetLayout* pSetLayouts;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkDescriptorBufferInfo
|
||||||
|
{
|
||||||
|
public VkBuffer buffer;
|
||||||
|
public ulong offset;
|
||||||
|
public ulong range;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkWriteDescriptorSet
|
||||||
|
{
|
||||||
|
public VkStructureType sType;
|
||||||
|
public nint pNext;
|
||||||
|
public VkDescriptorSet dstSet;
|
||||||
|
public uint dstBinding;
|
||||||
|
public uint dstArrayElement;
|
||||||
|
public uint descriptorCount;
|
||||||
|
public VkDescriptorType descriptorType;
|
||||||
|
public nint pImageInfo;
|
||||||
|
public VkDescriptorBufferInfo* pBufferInfo;
|
||||||
|
public nint pTexelBufferView;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user