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(row_major, set = 0, binding = 0) uniform CameraUBO {
|
||||
mat4 vp;
|
||||
};
|
||||
|
||||
layout(push_constant) uniform PC {
|
||||
float angle;
|
||||
} pc;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -62,6 +62,14 @@ internal static unsafe class Vk
|
||||
public delegate void VkCmdEndRendering(VkCommandBuffer commandBuffer);
|
||||
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 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 VkQueueSubmit2(VkQueue queue, uint submitCount, VkSubmitInfo2* pSubmits, VkFence fence);
|
||||
public delegate VkResult VkQueuePresentKHR(VkQueue queue, VkPresentInfoKHR* pPresentInfo);
|
||||
@@ -129,6 +137,14 @@ internal static unsafe class Vk
|
||||
public static VkCmdEndRendering vkCmdEndRendering;
|
||||
public static VkCmdPipelineBarrier2 vkCmdPipelineBarrier2;
|
||||
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 VkQueueSubmit2 vkQueueSubmit2;
|
||||
public static VkQueuePresentKHR vkQueuePresentKHR;
|
||||
@@ -203,6 +219,14 @@ internal static unsafe class Vk
|
||||
vkCmdEndRendering = LoadDev<VkCmdEndRendering>(p, "vkCmdEndRendering");
|
||||
vkCmdPipelineBarrier2 = LoadDev<VkCmdPipelineBarrier2>(p, "vkCmdPipelineBarrier2");
|
||||
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");
|
||||
vkQueueSubmit2 = LoadDev<VkQueueSubmit2>(p, "vkQueueSubmit2");
|
||||
vkQueuePresentKHR = LoadDev<VkQueuePresentKHR>(p, "vkQueuePresentKHR");
|
||||
|
||||
@@ -241,12 +241,13 @@ internal sealed unsafe class VulkanContext : IDisposable
|
||||
var nameLen = Array.IndexOf(nameBytes, (byte)0);
|
||||
if (nameLen < 0) nameLen = 256;
|
||||
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))
|
||||
{
|
||||
best = devices[(int)i];
|
||||
bestType = devType;
|
||||
Console.WriteLine($"[Vulkan] Selected GPU: {gpuName} (type={devType})");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +374,6 @@ internal sealed unsafe class VulkanContext : IDisposable
|
||||
SurfaceColorSpace = formats[0].colorSpace;
|
||||
}
|
||||
|
||||
Console.WriteLine($"[Vulkan] Surface format: {SurfaceFormat}, color space: {SurfaceColorSpace}");
|
||||
}
|
||||
|
||||
public uint FindMemoryType(uint memoryTypeBits, VkMemoryPropertyFlags desiredFlags)
|
||||
|
||||
@@ -39,6 +39,10 @@ public enum VkStructureType : int
|
||||
MemoryAllocateInfo = 5,
|
||||
BufferCreateInfo = 12,
|
||||
ShaderModuleCreateInfo = 16,
|
||||
DescriptorSetLayoutCreateInfo = 32,
|
||||
DescriptorPoolCreateInfo = 33,
|
||||
DescriptorSetAllocateInfo = 34,
|
||||
WriteDescriptorSet = 35,
|
||||
PipelineShaderStageCreateInfo = 18,
|
||||
PipelineVertexInputStateCreateInfo = 19,
|
||||
PipelineInputAssemblyStateCreateInfo = 20,
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Engine.Graphics.Vulkan;
|
||||
|
||||
internal sealed unsafe class VulkanFrameResources : IDisposable
|
||||
{
|
||||
public const int MaxFramesInFlight = 2;
|
||||
public const ulong UboSize = 64;
|
||||
|
||||
public VkCommandPool CommandPool;
|
||||
public VkCommandBuffer[] CommandBuffers = new VkCommandBuffer[MaxFramesInFlight];
|
||||
@@ -10,10 +13,17 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
||||
public VkSemaphore[] AcquireSemaphores = new VkSemaphore[MaxFramesInFlight];
|
||||
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 bool _disposed;
|
||||
|
||||
public VulkanFrameResources(VkDevice device, uint queueFamilyIndex, uint swapchainImageCount)
|
||||
public VulkanFrameResources(VkDevice device, uint queueFamilyIndex, uint swapchainImageCount,
|
||||
VulkanContext ctx, VkDescriptorSetLayout descriptorSetLayout)
|
||||
{
|
||||
_device = device;
|
||||
|
||||
@@ -24,12 +34,11 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
||||
queueFamilyIndex = queueFamilyIndex,
|
||||
};
|
||||
|
||||
fixed (VkCommandPool* poolPtr = &CommandPool)
|
||||
{
|
||||
var result = Vk.vkCreateCommandPool(_device, &poolInfo, 0, poolPtr);
|
||||
if (result != VkResult.Success)
|
||||
throw new InvalidOperationException($"vkCreateCommandPool failed: {result}");
|
||||
}
|
||||
var cmdPool = VkCommandPool.Null;
|
||||
var result = Vk.vkCreateCommandPool(_device, &poolInfo, 0, &cmdPool);
|
||||
if (result != VkResult.Success)
|
||||
throw new InvalidOperationException($"vkCreateCommandPool failed: {result}");
|
||||
CommandPool = cmdPool;
|
||||
|
||||
var allocInfo = new VkCommandBufferAllocateInfo
|
||||
{
|
||||
@@ -41,7 +50,7 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
||||
|
||||
fixed (VkCommandBuffer* cmdPtr = CommandBuffers)
|
||||
{
|
||||
var result = Vk.vkAllocateCommandBuffers(_device, &allocInfo, cmdPtr);
|
||||
result = Vk.vkAllocateCommandBuffers(_device, &allocInfo, cmdPtr);
|
||||
if (result != VkResult.Success)
|
||||
throw new InvalidOperationException($"vkAllocateCommandBuffers failed: {result}");
|
||||
}
|
||||
@@ -60,7 +69,7 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
||||
for (int i = 0; i < MaxFramesInFlight; i++)
|
||||
{
|
||||
var fence = VkFence.Null;
|
||||
var result = Vk.vkCreateFence(_device, &fenceInfo, 0, &fence);
|
||||
result = Vk.vkCreateFence(_device, &fenceInfo, 0, &fence);
|
||||
if (result != VkResult.Success)
|
||||
throw new InvalidOperationException($"vkCreateFence failed: {result}");
|
||||
FrameFences[i] = fence;
|
||||
@@ -76,15 +85,133 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
||||
for (int i = 0; i < swapchainImageCount; i++)
|
||||
{
|
||||
var sem = VkSemaphore.Null;
|
||||
var result = Vk.vkCreateSemaphore(_device, &semInfo, 0, &sem);
|
||||
result = Vk.vkCreateSemaphore(_device, &semInfo, 0, &sem);
|
||||
if (result != VkResult.Success)
|
||||
throw new InvalidOperationException($"vkCreateSemaphore (submit) failed: {result}");
|
||||
SubmitSemaphores[i] = sem;
|
||||
}
|
||||
|
||||
CreateUniformBuffers(ctx);
|
||||
CreateDescriptorPoolAndSets(descriptorSetLayout);
|
||||
|
||||
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)
|
||||
{
|
||||
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 (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++)
|
||||
@@ -112,6 +241,7 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
private bool _disposed;
|
||||
private bool _screenshotRequested;
|
||||
private string? _screenshotPath;
|
||||
private float _totalTime;
|
||||
|
||||
public bool IsScreenshotRequested => _screenshotRequested;
|
||||
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);
|
||||
|
||||
_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[]
|
||||
{
|
||||
@@ -69,6 +71,11 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
if (acquireResult != VkResult.Success)
|
||||
throw new InvalidOperationException($"vkAcquireNextImageKHR failed: {acquireResult}");
|
||||
|
||||
_totalTime += 0.016f;
|
||||
|
||||
var vp = ComputeViewProjection();
|
||||
_frameResources.UpdateUbo(_frameIndex, &vp, VulkanFrameResources.UboSize);
|
||||
|
||||
var cmd = _frameResources.CommandBuffers[_frameIndex];
|
||||
Vk.vkResetCommandBuffer(cmd, 0);
|
||||
|
||||
@@ -132,10 +139,17 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
};
|
||||
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;
|
||||
ulong offset = 0;
|
||||
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.vkCmdEndRendering(cmd);
|
||||
@@ -207,6 +221,14 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
_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,
|
||||
VkImageLayout oldLayout, VkImageLayout newLayout,
|
||||
ulong srcStage, ulong srcAccess,
|
||||
|
||||
@@ -907,3 +907,82 @@ public unsafe struct VkDebugUtilsMessengerCallbackDataEXT
|
||||
public uint objectCount;
|
||||
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