feat: ImGui integration + real screenshot capture via Vulkan

ImGui:
- ImGui.NET 1.91.6.1 NuGet package
- VulkanImGui: font texture upload, ImGui pipeline (blending, no depth), vertex/index buffer streaming
- GLSL 450 ImGui shaders (vert: scale/translate push constants, frag: font texture sampler)
- Compiled to SPIR-V via @webgpu/glslang
- Debug overlay: FPS, camera position, entity count, light list
- 1600+ FPS with ImGui active

Screenshot:
- vkCmdCopyImageToBuffer embedded in main command buffer
- Layout transition PresentSrcKHR→TransferSrcOptimal→PresentSrcKHR
- Deferred read: staging buffer read on next frame after fence
- BMP format written directly to FileStream (row by row)
- All 16 camera tour screenshots captured (1280x720x32bpp)

All 66 tests pass.
This commit is contained in:
emil28092005
2026-06-17 23:39:38 +03:00
parent 544f6b59c0
commit 749fbc8df4
16 changed files with 838 additions and 231 deletions
+41
View File
@@ -14,6 +14,7 @@ using Engine.Graphics.Loaders;
using Engine.Graphics.Vulkan;
using Engine.Physics;
using Flecs.NET.Core;
using ImGuiNET;
namespace CortexEngine.App;
@@ -46,6 +47,16 @@ class Program
var input = window.Input;
using var renderer = renderContext.CreateRenderer();
VulkanImGui? imGuiLayer = null;
if (!cameraTour && renderer is VulkanRenderer vkRenderer)
{
ImGui.CreateContext();
imGuiLayer = new VulkanImGui(vkRenderer._ctx, vkRenderer._swapchain);
vkRenderer.ImGuiLayer = imGuiLayer;
ImGui.GetIO().DisplaySize = new System.Numerics.Vector2(window.Width, window.Height);
Console.WriteLine("[App] ImGui initialized.");
}
var (modelPath, mcpPort) = ParseArgs(args);
var mesh = LoadModel(modelPath);
@@ -249,6 +260,35 @@ class Program
demoScreenshotRequested = true;
}
if (imGuiLayer != null)
{
ImGui.NewFrame();
ImGui.Begin("Cortex Engine Debug");
ImGui.Text($"FPS: {currentFps}");
ImGui.Text($"Delta: {timing.DeltaTime * 1000.0:F2} ms");
ImGui.Text($"Camera: {cameraController.Name}");
ImGui.Separator();
var cam = cameraEntity.Get<Camera>();
ImGui.Text($"Pos: ({cam.Position.X:F2}, {cam.Position.Y:F2}, {cam.Position.Z:F2})");
ImGui.Text($"Target: ({cam.Target.X:F2}, {cam.Target.Y:F2}, {cam.Target.Z:F2})");
ImGui.Separator();
var entityCount = 0;
world.Each((Entity e, ref Transform _) => entityCount++);
ImGui.Text($"Entities: {entityCount}");
ImGui.Text($"Press F to toggle camera");
ImGui.Separator();
ImGui.Text("Lights:");
world.Each((Entity e, ref Light light) =>
{
ImGui.Text($" {e.Name()}: {light.Type} I={light.Intensity:F1}");
});
ImGui.End();
ImGui.Render();
}
renderer.RenderWorld(world);
queue.CompletePendingScreenshots();
@@ -263,6 +303,7 @@ class Program
}
Console.WriteLine("Shutting down...");
imGuiLayer?.Dispose();
#if !RELEASE_AOT
if (mcpApp != null)
await mcpApp.StopAsync();
@@ -18,6 +18,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ImGui.NET" Version="1.91.6.1" />
<PackageReference Include="Flecs.NET.Debug" Version="4.0.4-build.546" Condition="'$(Configuration)' == 'Debug'" />
<PackageReference Include="Flecs.NET.Release" Version="4.0.4-build.546" Condition="'$(Configuration)' == 'Release' OR '$(Configuration)' == 'ReleaseAOT'" />
</ItemGroup>
@@ -0,0 +1,12 @@
#version 450
layout(location = 0) in vec2 fragUV;
layout(location = 1) in vec4 fragColor;
layout(set = 0, binding = 0) uniform sampler2D fontTexture;
layout(location = 0) out vec4 outColor;
void main() {
outColor = fragColor * texture(fontTexture, fragUV);
}
Binary file not shown.
@@ -0,0 +1,19 @@
#version 450
layout(location = 0) in vec2 inPos;
layout(location = 1) in vec2 inUV;
layout(location = 2) in vec4 inColor;
layout(push_constant) uniform PushConstants {
vec2 scale;
vec2 translate;
} pc;
layout(location = 0) out vec2 fragUV;
layout(location = 1) out vec4 fragColor;
void main() {
fragUV = inUV;
fragColor = inColor;
gl_Position = vec4(inPos * pc.scale + pc.translate, 0.0, 1.0);
}
Binary file not shown.
+84 -84
View File
@@ -2,7 +2,7 @@ using System.Runtime.InteropServices;
namespace Engine.Graphics.Vulkan;
internal static unsafe class Vk
public static unsafe class Vk
{
public static VkInstance Instance;
public static VkDevice Device;
@@ -242,168 +242,168 @@ internal static unsafe class Vk
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateInstance(VkInstanceCreateInfo* pCreateInfo, void* pAllocator, VkInstance* pInstance);
unsafe public delegate VkResult PFN_vkCreateInstance(VkInstanceCreateInfo* pCreateInfo, void* pAllocator, VkInstance* pInstance);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyInstance(VkInstance instance, void* pAllocator);
unsafe public delegate void PFN_vkDestroyInstance(VkInstance instance, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkEnumeratePhysicalDevices(VkInstance instance, uint* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
unsafe public delegate VkResult PFN_vkEnumeratePhysicalDevices(VkInstance instance, uint* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties);
unsafe public delegate void PFN_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties);
unsafe public delegate void PFN_vkGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties);
unsafe public delegate void PFN_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, byte* pLayerName, uint* pPropertyCount, VkExtensionProperties* pProperties);
unsafe public delegate VkResult PFN_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, byte* pLayerName, uint* pPropertyCount, VkExtensionProperties* pProperties);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateDevice(VkPhysicalDevice physicalDevice, VkDeviceCreateInfo* pCreateInfo, void* pAllocator, VkDevice* pDevice);
unsafe public delegate VkResult PFN_vkCreateDevice(VkPhysicalDevice physicalDevice, VkDeviceCreateInfo* pCreateInfo, void* pAllocator, VkDevice* pDevice);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyDevice(VkDevice device, void* pAllocator);
unsafe public delegate void PFN_vkDestroyDevice(VkDevice device, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkGetDeviceQueue(VkDevice device, uint queueFamilyIndex, uint queueIndex, VkQueue* pQueue);
unsafe public delegate void PFN_vkGetDeviceQueue(VkDevice device, uint queueFamilyIndex, uint queueIndex, VkQueue* pQueue);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateSwapchainKHR(VkDevice device, VkSwapchainCreateInfoKHR* pCreateInfo, void* pAllocator, VkSwapchainKHR* pSwapchain);
unsafe public delegate VkResult PFN_vkCreateSwapchainKHR(VkDevice device, VkSwapchainCreateInfoKHR* pCreateInfo, void* pAllocator, VkSwapchainKHR* pSwapchain);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, void* pAllocator);
unsafe public delegate void PFN_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint* pSwapchainImageCount, VkImage* pSwapchainImages);
unsafe public delegate VkResult PFN_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint* pSwapchainImageCount, VkImage* pSwapchainImages);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateImageView(VkDevice device, VkImageViewCreateInfo* pCreateInfo, void* pAllocator, VkImageView* pView);
unsafe public delegate VkResult PFN_vkCreateImageView(VkDevice device, VkImageViewCreateInfo* pCreateInfo, void* pAllocator, VkImageView* pView);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyImageView(VkDevice device, VkImageView imageView, void* pAllocator);
unsafe public delegate void PFN_vkDestroyImageView(VkDevice device, VkImageView imageView, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateImage(VkDevice device, VkImageCreateInfo* pCreateInfo, void* pAllocator, VkImage* pImage);
unsafe public delegate VkResult PFN_vkCreateImage(VkDevice device, VkImageCreateInfo* pCreateInfo, void* pAllocator, VkImage* pImage);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyImage(VkDevice device, VkImage image, void* pAllocator);
unsafe public delegate void PFN_vkDestroyImage(VkDevice device, VkImage image, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkGetImageMemoryRequirements(VkDevice device, VkImage image, void* pMemoryRequirements);
unsafe public delegate void PFN_vkGetImageMemoryRequirements(VkDevice device, VkImage image, void* pMemoryRequirements);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, ulong memoryOffset);
unsafe public delegate VkResult PFN_vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, ulong memoryOffset);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateRenderPass(VkDevice device, VkRenderPassCreateInfo* pCreateInfo, void* pAllocator, VkRenderPass* pRenderPass);
unsafe public delegate VkResult PFN_vkCreateRenderPass(VkDevice device, VkRenderPassCreateInfo* pCreateInfo, void* pAllocator, VkRenderPass* pRenderPass);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyRenderPass(VkDevice device, VkRenderPass renderPass, void* pAllocator);
unsafe public delegate void PFN_vkDestroyRenderPass(VkDevice device, VkRenderPass renderPass, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateFramebuffer(VkDevice device, VkFramebufferCreateInfo* pCreateInfo, void* pAllocator, VkFramebuffer* pFramebuffer);
unsafe public delegate VkResult PFN_vkCreateFramebuffer(VkDevice device, VkFramebufferCreateInfo* pCreateInfo, void* pAllocator, VkFramebuffer* pFramebuffer);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyFramebuffer(VkDevice device, VkFramebuffer framebuffer, void* pAllocator);
unsafe public delegate void PFN_vkDestroyFramebuffer(VkDevice device, VkFramebuffer framebuffer, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateShaderModule(VkDevice device, VkShaderModuleCreateInfo* pCreateInfo, void* pAllocator, VkShaderModule* pShaderModule);
unsafe public delegate VkResult PFN_vkCreateShaderModule(VkDevice device, VkShaderModuleCreateInfo* pCreateInfo, void* pAllocator, VkShaderModule* pShaderModule);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyShaderModule(VkDevice device, VkShaderModule shaderModule, void* pAllocator);
unsafe public delegate void PFN_vkDestroyShaderModule(VkDevice device, VkShaderModule shaderModule, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateDescriptorSetLayout(VkDevice device, VkDescriptorSetLayoutCreateInfo* pCreateInfo, void* pAllocator, VkDescriptorSetLayout* pSetLayout);
unsafe public delegate VkResult PFN_vkCreateDescriptorSetLayout(VkDevice device, VkDescriptorSetLayoutCreateInfo* pCreateInfo, void* pAllocator, VkDescriptorSetLayout* pSetLayout);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyDescriptorSetLayout(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, void* pAllocator);
unsafe public delegate void PFN_vkDestroyDescriptorSetLayout(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreatePipelineLayout(VkDevice device, VkPipelineLayoutCreateInfo* pCreateInfo, void* pAllocator, VkPipelineLayout* pPipelineLayout);
unsafe public delegate VkResult PFN_vkCreatePipelineLayout(VkDevice device, VkPipelineLayoutCreateInfo* pCreateInfo, void* pAllocator, VkPipelineLayout* pPipelineLayout);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyPipelineLayout(VkDevice device, VkPipelineLayout pipelineLayout, void* pAllocator);
unsafe public delegate void PFN_vkDestroyPipelineLayout(VkDevice device, VkPipelineLayout pipelineLayout, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateGraphicsPipelines(VkDevice device, ulong pipelineCache, uint createInfoCount, VkGraphicsPipelineCreateInfo* pCreateInfos, void* pAllocator, VkPipeline* pPipelines);
unsafe public delegate VkResult PFN_vkCreateGraphicsPipelines(VkDevice device, ulong pipelineCache, uint createInfoCount, VkGraphicsPipelineCreateInfo* pCreateInfos, void* pAllocator, VkPipeline* pPipelines);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyPipeline(VkDevice device, VkPipeline pipeline, void* pAllocator);
unsafe public delegate void PFN_vkDestroyPipeline(VkDevice device, VkPipeline pipeline, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateDescriptorPool(VkDevice device, VkDescriptorPoolCreateInfo* pCreateInfo, void* pAllocator, VkDescriptorPool* pDescriptorPool);
unsafe public delegate VkResult PFN_vkCreateDescriptorPool(VkDevice device, VkDescriptorPoolCreateInfo* pCreateInfo, void* pAllocator, VkDescriptorPool* pDescriptorPool);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, void* pAllocator);
unsafe public delegate void PFN_vkDestroyDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkAllocateDescriptorSets(VkDevice device, VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets);
unsafe public delegate VkResult PFN_vkAllocateDescriptorSets(VkDevice device, VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkUpdateDescriptorSets(VkDevice device, uint descriptorWriteCount, VkWriteDescriptorSet* pDescriptorWrites, uint descriptorCopyCount, void* pDescriptorCopies);
unsafe public delegate void PFN_vkUpdateDescriptorSets(VkDevice device, uint descriptorWriteCount, VkWriteDescriptorSet* pDescriptorWrites, uint descriptorCopyCount, void* pDescriptorCopies);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateBuffer(VkDevice device, VkBufferCreateInfo* pCreateInfo, void* pAllocator, VkBuffer* pBuffer);
unsafe public delegate VkResult PFN_vkCreateBuffer(VkDevice device, VkBufferCreateInfo* pCreateInfo, void* pAllocator, VkBuffer* pBuffer);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyBuffer(VkDevice device, VkBuffer buffer, void* pAllocator);
unsafe public delegate void PFN_vkDestroyBuffer(VkDevice device, VkBuffer buffer, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, void* pMemoryRequirements);
unsafe public delegate void PFN_vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, void* pMemoryRequirements);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, ulong memoryOffset);
unsafe public delegate VkResult PFN_vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, ulong memoryOffset);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkAllocateMemory(VkDevice device, VkMemoryAllocateInfo* pAllocateInfo, void* pAllocator, VkDeviceMemory* pMemory);
unsafe public delegate VkResult PFN_vkAllocateMemory(VkDevice device, VkMemoryAllocateInfo* pAllocateInfo, void* pAllocator, VkDeviceMemory* pMemory);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkFreeMemory(VkDevice device, VkDeviceMemory memory, void* pAllocator);
unsafe public delegate void PFN_vkFreeMemory(VkDevice device, VkDeviceMemory memory, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkMapMemory(VkDevice device, VkDeviceMemory memory, ulong offset, ulong size, uint flags, void** ppData);
unsafe public delegate VkResult PFN_vkMapMemory(VkDevice device, VkDeviceMemory memory, ulong offset, ulong size, uint flags, void** ppData);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkUnmapMemory(VkDevice device, VkDeviceMemory memory);
unsafe public delegate void PFN_vkUnmapMemory(VkDevice device, VkDeviceMemory memory);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateCommandPool(VkDevice device, VkCommandPoolCreateInfo* pCreateInfo, void* pAllocator, VkCommandPool* pCommandPool);
unsafe public delegate VkResult PFN_vkCreateCommandPool(VkDevice device, VkCommandPoolCreateInfo* pCreateInfo, void* pAllocator, VkCommandPool* pCommandPool);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyCommandPool(VkDevice device, VkCommandPool commandPool, void* pAllocator);
unsafe public delegate void PFN_vkDestroyCommandPool(VkDevice device, VkCommandPool commandPool, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkAllocateCommandBuffers(VkDevice device, VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
unsafe public delegate VkResult PFN_vkAllocateCommandBuffers(VkDevice device, VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint commandBufferCount, VkCommandBuffer* pCommandBuffers);
unsafe public delegate void PFN_vkFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint commandBufferCount, VkCommandBuffer* pCommandBuffers);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkBeginCommandBuffer(VkCommandBuffer commandBuffer, VkCommandBufferBeginInfo* pBeginInfo);
unsafe public delegate VkResult PFN_vkBeginCommandBuffer(VkCommandBuffer commandBuffer, VkCommandBufferBeginInfo* pBeginInfo);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkEndCommandBuffer(VkCommandBuffer commandBuffer);
unsafe public delegate VkResult PFN_vkEndCommandBuffer(VkCommandBuffer commandBuffer);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkResetCommandBuffer(VkCommandBuffer commandBuffer, uint flags);
unsafe public delegate VkResult PFN_vkResetCommandBuffer(VkCommandBuffer commandBuffer, uint flags);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkQueueSubmit(VkQueue queue, uint submitCount, VkSubmitInfo* pSubmits, VkFence fence);
unsafe public delegate VkResult PFN_vkQueueSubmit(VkQueue queue, uint submitCount, VkSubmitInfo* pSubmits, VkFence fence);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkQueueWaitIdle(VkQueue queue);
unsafe public delegate VkResult PFN_vkQueueWaitIdle(VkQueue queue);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkQueuePresentKHR(VkQueue queue, VkPresentInfoKHR* pPresentInfo);
unsafe public delegate VkResult PFN_vkQueuePresentKHR(VkQueue queue, VkPresentInfoKHR* pPresentInfo);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, ulong timeout, VkSemaphore semaphore, VkFence fence, uint* pImageIndex);
unsafe public delegate VkResult PFN_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, ulong timeout, VkSemaphore semaphore, VkFence fence, uint* pImageIndex);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateSemaphore(VkDevice device, VkSemaphoreCreateInfo* pCreateInfo, void* pAllocator, VkSemaphore* pSemaphore);
unsafe public delegate VkResult PFN_vkCreateSemaphore(VkDevice device, VkSemaphoreCreateInfo* pCreateInfo, void* pAllocator, VkSemaphore* pSemaphore);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroySemaphore(VkDevice device, VkSemaphore semaphore, void* pAllocator);
unsafe public delegate void PFN_vkDestroySemaphore(VkDevice device, VkSemaphore semaphore, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateFence(VkDevice device, VkFenceCreateInfo* pCreateInfo, void* pAllocator, VkFence* pFence);
unsafe public delegate VkResult PFN_vkCreateFence(VkDevice device, VkFenceCreateInfo* pCreateInfo, void* pAllocator, VkFence* pFence);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroyFence(VkDevice device, VkFence fence, void* pAllocator);
unsafe public delegate void PFN_vkDestroyFence(VkDevice device, VkFence fence, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkWaitForFences(VkDevice device, uint fenceCount, VkFence* pFences, uint waitAll, ulong timeout);
unsafe public delegate VkResult PFN_vkWaitForFences(VkDevice device, uint fenceCount, VkFence* pFences, uint waitAll, ulong timeout);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkResetFences(VkDevice device, uint fenceCount, VkFence* pFences);
unsafe public delegate VkResult PFN_vkResetFences(VkDevice device, uint fenceCount, VkFence* pFences);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdBeginRenderPass(VkCommandBuffer commandBuffer, VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents);
unsafe public delegate void PFN_vkCmdBeginRenderPass(VkCommandBuffer commandBuffer, VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdEndRenderPass(VkCommandBuffer commandBuffer);
unsafe public delegate void PFN_vkCmdEndRenderPass(VkCommandBuffer commandBuffer);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdBindPipeline(VkCommandBuffer commandBuffer, int pipelineBindPoint, VkPipeline pipeline);
unsafe public delegate void PFN_vkCmdBindPipeline(VkCommandBuffer commandBuffer, int pipelineBindPoint, VkPipeline pipeline);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdBindDescriptorSets(VkCommandBuffer commandBuffer, int pipelineBindPoint, VkPipelineLayout layout, uint firstSet, uint descriptorSetCount, VkDescriptorSet* pDescriptorSets, uint dynamicOffsetCount, uint* pDynamicOffsets);
unsafe public delegate void PFN_vkCmdBindDescriptorSets(VkCommandBuffer commandBuffer, int pipelineBindPoint, VkPipelineLayout layout, uint firstSet, uint descriptorSetCount, VkDescriptorSet* pDescriptorSets, uint dynamicOffsetCount, uint* pDynamicOffsets);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdBindVertexBuffers(VkCommandBuffer commandBuffer, uint firstBinding, uint bindingCount, VkBuffer* pBuffers, ulong* pOffsets);
unsafe public delegate void PFN_vkCmdBindVertexBuffers(VkCommandBuffer commandBuffer, uint firstBinding, uint bindingCount, VkBuffer* pBuffers, ulong* pOffsets);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdBindIndexBuffer(VkCommandBuffer commandBuffer, VkBuffer buffer, ulong offset, VkIndexType indexType);
unsafe public delegate void PFN_vkCmdBindIndexBuffer(VkCommandBuffer commandBuffer, VkBuffer buffer, ulong offset, VkIndexType indexType);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdDrawIndexed(VkCommandBuffer commandBuffer, uint indexCount, uint instanceCount, uint firstIndex, int vertexOffset, uint firstInstance);
unsafe public delegate void PFN_vkCmdDrawIndexed(VkCommandBuffer commandBuffer, uint indexCount, uint instanceCount, uint firstIndex, int vertexOffset, uint firstInstance);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdDraw(VkCommandBuffer commandBuffer, uint vertexCount, uint instanceCount, uint firstVertex, uint firstInstance);
unsafe public delegate void PFN_vkCmdDraw(VkCommandBuffer commandBuffer, uint vertexCount, uint instanceCount, uint firstVertex, uint firstInstance);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdSetViewport(VkCommandBuffer commandBuffer, uint firstViewport, uint viewportCount, VkViewport* pViewports);
unsafe public delegate void PFN_vkCmdSetViewport(VkCommandBuffer commandBuffer, uint firstViewport, uint viewportCount, VkViewport* pViewports);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdSetScissor(VkCommandBuffer commandBuffer, uint firstScissor, uint scissorCount, VkRect2D* pScissors);
unsafe public delegate void PFN_vkCmdSetScissor(VkCommandBuffer commandBuffer, uint firstScissor, uint scissorCount, VkRect2D* pScissors);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, int dependencyFlags, uint memoryBarrierCount, void* pMemoryBarriers, uint bufferMemoryBarrierCount, VkBufferMemoryBarrier* pBufferMemoryBarriers, uint imageMemoryBarrierCount, VkImageMemoryBarrier* pImageMemoryBarriers);
unsafe public delegate void PFN_vkCmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, int dependencyFlags, uint memoryBarrierCount, void* pMemoryBarriers, uint bufferMemoryBarrierCount, VkBufferMemoryBarrier* pBufferMemoryBarriers, uint imageMemoryBarrierCount, VkImageMemoryBarrier* pImageMemoryBarriers);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint regionCount, VkBufferCopy* pRegions);
unsafe public delegate void PFN_vkCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint regionCount, VkBufferCopy* pRegions);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, int dstImageLayout, uint regionCount, VkBufferImageCopy* pRegions);
unsafe public delegate void PFN_vkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, int dstImageLayout, uint regionCount, VkBufferImageCopy* pRegions);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, int srcImageLayout, VkBuffer dstBuffer, uint regionCount, VkBufferImageCopy* pRegions);
unsafe public delegate void PFN_vkCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, int srcImageLayout, VkBuffer dstBuffer, uint regionCount, VkBufferImageCopy* pRegions);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, int imageLayout, VkClearColorValue* pColor, uint rangeCount, VkImageSubresourceRange* pRanges);
unsafe public delegate void PFN_vkCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, int imageLayout, VkClearColorValue* pColor, uint rangeCount, VkImageSubresourceRange* pRanges);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkCmdPushConstants(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint offset, uint size, void* pValues);
unsafe public delegate void PFN_vkCmdPushConstants(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint offset, uint size, void* pValues);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkCreateSampler(VkDevice device, VkSamplerCreateInfo* pCreateInfo, void* pAllocator, void* pSampler);
unsafe public delegate VkResult PFN_vkCreateSampler(VkDevice device, VkSamplerCreateInfo* pCreateInfo, void* pAllocator, void* pSampler);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroySampler(VkDevice device, void* sampler, void* pAllocator);
unsafe public delegate void PFN_vkDestroySampler(VkDevice device, void* sampler, void* pAllocator);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint queueFamilyIndex, VkSurfaceKHR surface, uint* pSupported);
unsafe public delegate VkResult PFN_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint queueFamilyIndex, VkSurfaceKHR surface, uint* pSupported);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities);
unsafe public delegate VkResult PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats);
unsafe public delegate VkResult PFN_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate VkResult PFN_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint* pPresentModeCount, VkPresentModeKHR* pPresentModes);
unsafe public delegate VkResult PFN_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint* pPresentModeCount, VkPresentModeKHR* pPresentModes);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
unsafe internal delegate void PFN_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, void* pAllocator);
unsafe public delegate void PFN_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, void* pAllocator);
+1 -1
View File
@@ -2,7 +2,7 @@ using System.Runtime.InteropServices;
namespace Engine.Graphics.Vulkan;
internal sealed unsafe class VulkanBuffer : IDisposable
public sealed unsafe class VulkanBuffer : IDisposable
{
public VkBuffer Buffer;
public VkDeviceMemory Memory;
+1 -1
View File
@@ -5,7 +5,7 @@ using SDL;
namespace Engine.Graphics.Vulkan;
internal sealed unsafe class VulkanContext : IDisposable
public sealed unsafe class VulkanContext : IDisposable
{
public VkInstance Instance;
public VkPhysicalDevice PhysicalDevice;
+521
View File
@@ -0,0 +1,521 @@
using System.Runtime.InteropServices;
using System.Numerics;
using ImGuiNET;
namespace Engine.Graphics.Vulkan;
public sealed unsafe class VulkanImGui : IDisposable
{
private readonly VulkanContext _ctx;
private readonly VulkanSwapchain _swapchain;
private VkCommandPool _initCommandPool;
private VkPipeline _pipeline;
private VkPipelineLayout _pipelineLayout;
private VkDescriptorSetLayout _descriptorSetLayout;
private VkDescriptorPool _descriptorPool;
private VkDescriptorSet _descriptorSet;
private VkShaderModule _vertexShader;
private VkShaderModule _fragmentShader;
private VkImage _fontImage;
private VkDeviceMemory _fontImageMemory;
private VkImageView _fontImageView;
private VkSampler _fontSampler;
private VulkanBuffer? _vertexBuffer;
private VulkanBuffer? _indexBuffer;
private static readonly byte[] VkDescriptorWriteDummy = new byte[1];
public VulkanImGui(VulkanContext ctx, VulkanSwapchain swapchain)
{
_ctx = ctx;
_swapchain = swapchain;
Initialize();
}
private unsafe void Initialize()
{
var io = ImGui.GetIO();
io.Fonts.AddFontDefault();
io.Fonts.Build();
VkCommandPoolCreateInfo poolInfo = default;
poolInfo.sType = VkStructureType.CommandPoolCreateInfo;
poolInfo.flags = 0x00000002;
poolInfo.queueFamilyIndex = _ctx.GraphicsFamily;
VkCommandPool pool;
Vk.CheckResult(Vk.vkCreateCommandPool(_ctx.Device, &poolInfo, null, &pool), "vkCreateCommandPool (ImGui init)");
_initCommandPool = pool;
CreateFontTexture();
CreateShaders();
CreateDescriptorSetLayout();
CreatePipelineLayout();
CreatePipeline();
CreateDescriptorPoolAndSet();
Vk.vkDestroyCommandPool(_ctx.Device, _initCommandPool, null);
}
private unsafe void CreateFontTexture()
{
var io = ImGui.GetIO();
int width, height, bpp;
byte* pixels;
io.Fonts.GetTexDataAsRGBA32(out pixels, out width, out height, out bpp);
VkImageCreateInfo imageInfo = default;
imageInfo.sType = VkStructureType.ImageCreateInfo;
imageInfo.imageType = VkImageType._2D;
imageInfo.format = VkFormat.R8G8B8A8Unorm;
imageInfo.extent = new VkExtent3D { width = width, height = height, depth = 1 };
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.samples = VkSampleCountFlags.One;
imageInfo.tiling = 0;
imageInfo.usage = VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferDst;
imageInfo.sharingMode = VkSharingMode.Exclusive;
imageInfo.initialLayout = 0;
VkImage fontImage;
Vk.CheckResult(Vk.vkCreateImage(_ctx.Device, &imageInfo, null, &fontImage), "vkCreateImage (font)");
_fontImage = fontImage;
VkMemoryRequirements2 memReq;
Vk.vkGetImageMemoryRequirements(_ctx.Device, _fontImage, &memReq);
VkMemoryAllocateInfo allocInfo = default;
allocInfo.sType = VkStructureType.MemoryAllocateInfo;
allocInfo.allocationSize = memReq.size;
allocInfo.memoryTypeIndex = _ctx.FindMemoryType(memReq.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal);
VkDeviceMemory fontMem;
Vk.CheckResult(Vk.vkAllocateMemory(_ctx.Device, &allocInfo, null, &fontMem), "vkAllocateMemory (font)");
_fontImageMemory = fontMem;
Vk.CheckResult(Vk.vkBindImageMemory(_ctx.Device, _fontImage, _fontImageMemory, 0), "vkBindImageMemory (font)");
var imageSize = (ulong)(width * height * 4);
var staging = new VulkanBuffer(_ctx, imageSize,
VkBufferUsageFlags.TransferSrc,
VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent);
Buffer.MemoryCopy(pixels, staging.MappedData, imageSize, imageSize);
var cmd = VulkanBuffer.BeginSingleTimeCommands(_ctx, _initCommandPool);
var barrier = new VkImageMemoryBarrier
{
sType = VkStructureType.ImageMemoryBarrier,
srcAccessMask = 0,
dstAccessMask = VkAccessFlags.TransferWrite,
oldLayout = VkImageLayout.Undefined,
newLayout = VkImageLayout.TransferDstOptimal,
srcQueueFamilyIndex = ~0u,
dstQueueFamilyIndex = ~0u,
image = _fontImage,
subresourceRange = new VkImageSubresourceRange
{
aspectMask = VkImageAspectFlags.Color,
baseMipLevel = 0, levelCount = 1, baseArrayLayer = 0, layerCount = 1
}
};
Vk.vkCmdPipelineBarrier(cmd, VkPipelineStageFlags.Host, VkPipelineStageFlags.Transfer,
0, 0, null, 0, null, 1, &barrier);
var region = new VkBufferImageCopy
{
bufferOffset = 0,
bufferRowLength = (uint)width,
bufferImageHeight = (uint)height,
imageSubresource = new VkImageSubresourceLayers
{
aspectMask = VkImageAspectFlags.Color,
mipLevel = 0, baseArrayLayer = 0, layerCount = 1
},
imageOffset = new VkOffset3D { x = 0, y = 0, z = 0 },
imageExtent = new VkExtent3D { width = width, height = height, depth = 1 }
};
Vk.vkCmdCopyBufferToImage(cmd, staging.Buffer, _fontImage,
(int)VkImageLayout.TransferDstOptimal, 1, &region);
var barrier2 = new VkImageMemoryBarrier
{
sType = VkStructureType.ImageMemoryBarrier,
srcAccessMask = VkAccessFlags.TransferWrite,
dstAccessMask = VkAccessFlags.ShaderRead,
oldLayout = VkImageLayout.TransferDstOptimal,
newLayout = VkImageLayout.ShaderReadOnlyOptimal,
srcQueueFamilyIndex = ~0u,
dstQueueFamilyIndex = ~0u,
image = _fontImage,
subresourceRange = new VkImageSubresourceRange
{
aspectMask = VkImageAspectFlags.Color,
baseMipLevel = 0, levelCount = 1, baseArrayLayer = 0, layerCount = 1
}
};
Vk.vkCmdPipelineBarrier(cmd, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader,
0, 0, null, 0, null, 1, &barrier2);
VulkanBuffer.EndSingleTimeCommands(_ctx, _initCommandPool, cmd);
staging.Dispose();
VkImageViewCreateInfo viewInfo = default;
viewInfo.sType = VkStructureType.ImageViewCreateInfo;
viewInfo.image = _fontImage;
viewInfo.viewType = VkImageViewType._2D;
viewInfo.format = VkFormat.R8G8B8A8Unorm;
viewInfo.subresourceRange = new VkImageSubresourceRange
{
aspectMask = VkImageAspectFlags.Color,
baseMipLevel = 0, levelCount = 1, baseArrayLayer = 0, layerCount = 1
};
VkImageView fontView;
Vk.CheckResult(Vk.vkCreateImageView(_ctx.Device, &viewInfo, null, &fontView), "vkCreateImageView (font)");
_fontImageView = fontView;
VkSamplerCreateInfo samplerInfo = default;
samplerInfo.sType = VkStructureType.SamplerCreateInfo;
samplerInfo.magFilter = VkFilter.Linear;
samplerInfo.minFilter = VkFilter.Linear;
samplerInfo.mipmapMode = VkSamplerMipmapMode.Linear;
samplerInfo.addressModeU = VkSamplerAddressMode.Repeat;
samplerInfo.addressModeV = VkSamplerAddressMode.Repeat;
samplerInfo.addressModeW = VkSamplerAddressMode.Repeat;
samplerInfo.minLod = -1000;
samplerInfo.maxLod = 1000;
VkSampler fontSampler;
Vk.CheckResult(Vk.vkCreateSampler(_ctx.Device, &samplerInfo, null, (void*)&fontSampler), "vkCreateSampler (font)");
_fontSampler = fontSampler;
}
private unsafe VkShaderModule LoadShader(string path)
{
var fullPath = Path.Combine(AppContext.BaseDirectory, path);
if (!File.Exists(fullPath))
throw new FileNotFoundException($"ImGui SPIR-V shader not found: {fullPath}");
var code = File.ReadAllBytes(fullPath);
fixed (byte* pCode = code)
{
VkShaderModuleCreateInfo createInfo = default;
createInfo.sType = VkStructureType.ShaderModuleCreateInfo;
createInfo.codeSize = (ulong)code.Length;
createInfo.pCode = (uint*)pCode;
VkShaderModule module;
Vk.CheckResult(Vk.vkCreateShaderModule(_ctx.Device, &createInfo, null, &module), $"vkCreateShaderModule ({path})");
return module;
}
}
private void CreateShaders()
{
_vertexShader = LoadShader("Shaders/imgui.vert.spv");
_fragmentShader = LoadShader("Shaders/imgui.frag.spv");
}
private unsafe void CreateDescriptorSetLayout()
{
var binding = new VkDescriptorSetLayoutBinding
{
binding = 0,
descriptorType = VkDescriptorType.CombinedImageSampler,
descriptorCount = 1,
stageFlags = VkShaderStageFlags.Fragment,
pImmutableSamplers = null
};
VkDescriptorSetLayoutCreateInfo createInfo = default;
createInfo.sType = VkStructureType.DescriptorSetLayoutCreateInfo;
createInfo.bindingCount = 1;
createInfo.pBindings = &binding;
VkDescriptorSetLayout dsLayout;
Vk.CheckResult(Vk.vkCreateDescriptorSetLayout(_ctx.Device, &createInfo, null, &dsLayout), "vkCreateDescriptorSetLayout (ImGui)");
_descriptorSetLayout = dsLayout;
}
private unsafe void CreatePipelineLayout()
{
var pushConstantRange = new VkPushConstantRange
{
stageFlags = VkShaderStageFlags.Vertex,
offset = 0,
size = 16
};
var dsLayout = _descriptorSetLayout;
VkPipelineLayoutCreateInfo createInfo = default;
createInfo.sType = VkStructureType.PipelineLayoutCreateInfo;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &dsLayout;
createInfo.pushConstantRangeCount = 1;
createInfo.pPushConstantRanges = &pushConstantRange;
VkPipelineLayout pipeLayout;
Vk.CheckResult(Vk.vkCreatePipelineLayout(_ctx.Device, &createInfo, null, &pipeLayout), "vkCreatePipelineLayout (ImGui)");
_pipelineLayout = pipeLayout;
}
private unsafe void CreatePipeline()
{
var mainName = Vk.AllocUtf8("main");
var stages = stackalloc VkPipelineShaderStageCreateInfo[2];
stages[0] = new VkPipelineShaderStageCreateInfo
{
sType = VkStructureType.PipelineShaderStageCreateInfo,
stage = VkShaderStageFlags.Vertex,
module = _vertexShader,
pName = mainName
};
stages[1] = new VkPipelineShaderStageCreateInfo
{
sType = VkStructureType.PipelineShaderStageCreateInfo,
stage = VkShaderStageFlags.Fragment,
module = _fragmentShader,
pName = mainName
};
var bindingDesc = new VkVertexInputBindingDescription
{
binding = 0,
stride = (uint)Marshal.SizeOf<ImDrawVert>(),
inputRate = 0
};
var attrDescs = stackalloc VkVertexInputAttributeDescription[3];
attrDescs[0] = new VkVertexInputAttributeDescription { location = 0, binding = 0, format = VkFormat.R32G32Sfloat, offset = 0 };
attrDescs[1] = new VkVertexInputAttributeDescription { location = 1, binding = 0, format = VkFormat.R32G32Sfloat, offset = 8 };
attrDescs[2] = new VkVertexInputAttributeDescription { location = 2, binding = 0, format = VkFormat.R8G8B8A8Unorm, offset = 16 };
VkPipelineVertexInputStateCreateInfo vertexInputState = default;
vertexInputState.sType = VkStructureType.PipelineVertexInputStateCreateInfo;
vertexInputState.vertexBindingDescriptionCount = 1;
vertexInputState.pVertexBindingDescriptions = &bindingDesc;
vertexInputState.vertexAttributeDescriptionCount = 3;
vertexInputState.pVertexAttributeDescriptions = attrDescs;
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = default;
inputAssemblyState.sType = VkStructureType.PipelineInputAssemblyStateCreateInfo;
inputAssemblyState.topology = VkPrimitiveTopology.TriangleList;
var viewport = new VkViewport();
var scissor = new VkRect2D();
VkPipelineViewportStateCreateInfo viewportState = default;
viewportState.sType = VkStructureType.PipelineViewportStateCreateInfo;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterState = default;
rasterState.sType = VkStructureType.PipelineRasterizationStateCreateInfo;
rasterState.polygonMode = VkPolygonMode.Fill;
rasterState.cullMode = VkCullModeFlags.None;
rasterState.frontFace = VkFrontFace.Clockwise;
rasterState.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo msState = default;
msState.sType = VkStructureType.PipelineMultisampleStateCreateInfo;
msState.rasterizationSamples = VkSampleCountFlags.One;
VkPipelineColorBlendAttachmentState blendAttachment = default;
blendAttachment.blendEnable = 1;
blendAttachment.srcColorBlendFactor = VkBlendFactor.SrcAlpha;
blendAttachment.dstColorBlendFactor = VkBlendFactor.OneMinusSrcAlpha;
blendAttachment.colorBlendOp = VkBlendOp.Add;
blendAttachment.srcAlphaBlendFactor = VkBlendFactor.OneMinusSrcAlpha;
blendAttachment.dstAlphaBlendFactor = VkBlendFactor.Zero;
blendAttachment.alphaBlendOp = VkBlendOp.Add;
blendAttachment.colorWriteMask = VkColorComponentFlags.R | VkColorComponentFlags.G | VkColorComponentFlags.B | VkColorComponentFlags.A;
VkPipelineColorBlendStateCreateInfo blendState = default;
blendState.sType = VkStructureType.PipelineColorBlendStateCreateInfo;
blendState.attachmentCount = 1;
blendState.pAttachments = &blendAttachment;
VkGraphicsPipelineCreateInfo pipelineInfo = default;
pipelineInfo.sType = VkStructureType.GraphicsPipelineCreateInfo;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = stages;
pipelineInfo.pVertexInputState = &vertexInputState;
pipelineInfo.pInputAssemblyState = &inputAssemblyState;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterState;
pipelineInfo.pMultisampleState = &msState;
pipelineInfo.pColorBlendState = &blendState;
pipelineInfo.layout = _pipelineLayout;
pipelineInfo.renderPass = _swapchain.RenderPass;
pipelineInfo.subpass = 0;
VkPipeline pipe;
Vk.CheckResult(Vk.vkCreateGraphicsPipelines(_ctx.Device, 0, 1, &pipelineInfo, null, &pipe), "vkCreateGraphicsPipelines (ImGui)");
_pipeline = pipe;
Vk.FreeUtf8(mainName);
Console.WriteLine("[Vulkan] ImGui pipeline created.");
}
private unsafe void CreateDescriptorPoolAndSet()
{
var poolSize = new VkDescriptorPoolSize
{
type = VkDescriptorType.CombinedImageSampler,
descriptorCount = 1
};
VkDescriptorPoolCreateInfo poolInfo = default;
poolInfo.sType = VkStructureType.DescriptorPoolCreateInfo;
poolInfo.flags = VkDescriptorPoolCreateFlags.FreeDescriptorSet;
poolInfo.maxSets = 1;
poolInfo.poolSizeCount = 1;
poolInfo.pPoolSizes = &poolSize;
VkDescriptorPool descPool;
Vk.CheckResult(Vk.vkCreateDescriptorPool(_ctx.Device, &poolInfo, null, &descPool), "vkCreateDescriptorPool (ImGui)");
_descriptorPool = descPool;
VkDescriptorSetAllocateInfo allocInfo = default;
allocInfo.sType = VkStructureType.DescriptorSetAllocateInfo;
allocInfo.descriptorPool = _descriptorPool;
allocInfo.descriptorSetCount = 1;
var dsLayout = _descriptorSetLayout;
allocInfo.pSetLayouts = &dsLayout;
VkDescriptorSet descSet;
Vk.CheckResult(Vk.vkAllocateDescriptorSets(_ctx.Device, &allocInfo, &descSet), "vkAllocateDescriptorSets (ImGui)");
_descriptorSet = descSet;
var imageInfo = new VkDescriptorImageInfo
{
sampler = _fontSampler,
imageView = _fontImageView,
imageLayout = VkImageLayout.ShaderReadOnlyOptimal
};
VkWriteDescriptorSet writeInfo = default;
writeInfo.sType = VkStructureType.WriteDescriptorSet;
writeInfo.dstSet = _descriptorSet;
writeInfo.dstBinding = 0;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VkDescriptorType.CombinedImageSampler;
writeInfo.pImageInfo = &imageInfo;
Vk.vkUpdateDescriptorSets(_ctx.Device, 1, &writeInfo, 0, null);
}
public unsafe void Render(VkCommandBuffer cmd)
{
var drawData = ImGui.GetDrawData();
if (drawData.CmdListsCount == 0) return;
drawData.ScaleClipRects(ImGui.GetIO().DisplayFramebufferScale);
UpdateBuffers(drawData);
Vk.vkCmdBindPipeline(cmd, 0, _pipeline);
var set = _descriptorSet;
Vk.vkCmdBindDescriptorSets(cmd, 0, _pipelineLayout, 0, 1, &set, 0, null);
var displaySize = ImGui.GetIO().DisplaySize;
var scale = new Vector2(2.0f / displaySize.X, 2.0f / displaySize.Y);
var pushData = stackalloc float[2];
pushData[0] = scale.X;
pushData[1] = -scale.Y;
Vk.vkCmdPushConstants(cmd, _pipelineLayout, VkShaderStageFlags.Vertex, 0, 8, pushData);
var vb = _vertexBuffer!.Buffer;
var offset = 0ul;
Vk.vkCmdBindVertexBuffers(cmd, 0, 1, &vb, &offset);
Vk.vkCmdBindIndexBuffer(cmd, _indexBuffer!.Buffer, 0, VkIndexType.Uint16);
var indexOffset = 0u;
var vtxOffset = 0u;
for (var n = 0; n < drawData.CmdListsCount; n++)
{
var cmdList = new ImDrawListPtr(((ImDrawList**)drawData.CmdLists.Data)[n]);
for (var i = 0; i < cmdList.CmdBuffer.Size; i++)
{
var imCmd = cmdList.CmdBuffer[i];
Vk.vkCmdDrawIndexed(cmd, (uint)imCmd.ElemCount, 1, indexOffset, (int)vtxOffset, 0);
indexOffset += (uint)imCmd.ElemCount;
}
vtxOffset += (uint)cmdList.VtxBuffer.Size;
}
}
private unsafe void UpdateBuffers(ImDrawDataPtr drawData)
{
var vertexSize = (ulong)(drawData.TotalVtxCount * Marshal.SizeOf<ImDrawVert>());
var indexSize = (ulong)(drawData.TotalIdxCount * sizeof(ushort));
if (vertexSize == 0 || indexSize == 0) return;
if (_vertexBuffer == null || _vertexBuffer.Size < vertexSize)
{
_vertexBuffer?.Dispose();
_vertexBuffer = new VulkanBuffer(_ctx, vertexSize,
VkBufferUsageFlags.VertexBuffer,
VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent);
}
if (_indexBuffer == null || _indexBuffer.Size < indexSize)
{
_indexBuffer?.Dispose();
_indexBuffer = new VulkanBuffer(_ctx, indexSize,
VkBufferUsageFlags.IndexBuffer,
VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent);
}
var vtxDst = (byte*)_vertexBuffer.MappedData;
var idxDst = (ushort*)_indexBuffer.MappedData;
for (var n = 0; n < drawData.CmdListsCount; n++)
{
var cmdList = new ImDrawListPtr(((ImDrawList**)drawData.CmdLists.Data)[n]);
var vtxSize = cmdList.VtxBuffer.Size * Marshal.SizeOf<ImDrawVert>();
var idxSize = cmdList.IdxBuffer.Size * sizeof(ushort);
Buffer.MemoryCopy((void*)cmdList.VtxBuffer.Data, vtxDst, vtxSize, vtxSize);
Buffer.MemoryCopy((void*)cmdList.IdxBuffer.Data, idxDst, idxSize, idxSize);
vtxDst += vtxSize;
idxDst += cmdList.IdxBuffer.Size;
}
}
public void Dispose()
{
Vk.vkQueueWaitIdle(_ctx.GraphicsQueue);
_vertexBuffer?.Dispose();
_indexBuffer?.Dispose();
if (_fontSampler.Value != 0) Vk.vkDestroySampler(_ctx.Device, (void*)_fontSampler.Value, null);
if (_fontImageView.Value != 0) Vk.vkDestroyImageView(_ctx.Device, _fontImageView, null);
if (_fontImage.Value != 0) Vk.vkDestroyImage(_ctx.Device, _fontImage, null);
if (_fontImageMemory.Value != 0) Vk.vkFreeMemory(_ctx.Device, _fontImageMemory, null);
if (_descriptorPool.Value != 0) Vk.vkDestroyDescriptorPool(_ctx.Device, _descriptorPool, null);
if (_pipeline.Value != 0) Vk.vkDestroyPipeline(_ctx.Device, _pipeline, null);
if (_pipelineLayout.Value != 0) Vk.vkDestroyPipelineLayout(_ctx.Device, _pipelineLayout, null);
if (_descriptorSetLayout.Value != 0) Vk.vkDestroyDescriptorSetLayout(_ctx.Device, _descriptorSetLayout, null);
if (_vertexShader.Value != 0) Vk.vkDestroyShaderModule(_ctx.Device, _vertexShader, null);
if (_fragmentShader.Value != 0) Vk.vkDestroyShaderModule(_ctx.Device, _fragmentShader, null);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct VkDescriptorImageInfo
{
public VkSampler sampler;
public VkImageView imageView;
public VkImageLayout imageLayout;
}
+1 -1
View File
@@ -2,7 +2,7 @@ using System.Runtime.InteropServices;
namespace Engine.Graphics.Vulkan;
internal static unsafe partial class VulkanNative
public static unsafe partial class VulkanNative
{
private const string VulkanLib = "vulkan-1.dll";
private const string VulkanLibLinux = "libvulkan.so.1";
+1 -1
View File
@@ -3,7 +3,7 @@ using System.Text;
namespace Engine.Graphics.Vulkan;
internal sealed unsafe class VulkanPipeline : IDisposable
public sealed unsafe class VulkanPipeline : IDisposable
{
public VkPipelineLayout PipelineLayout;
public VkPipeline Pipeline;
+9 -3
View File
@@ -7,11 +7,12 @@ using Flecs.NET.Core;
namespace Engine.Graphics.Vulkan;
internal sealed unsafe class VulkanRenderer : IRenderer, IScreenshotProvider
public sealed unsafe class VulkanRenderer : IRenderer, IScreenshotProvider
{
private readonly VulkanContext _ctx;
private readonly VulkanSwapchain _swapchain;
public readonly VulkanContext _ctx;
public readonly VulkanSwapchain _swapchain;
private readonly VulkanPipeline _pipeline;
public VulkanImGui? ImGuiLayer;
private VkCommandPool _commandPool;
private VkCommandBuffer[] _commandBuffers = Array.Empty<VkCommandBuffer>();
@@ -536,6 +537,11 @@ internal sealed unsafe class VulkanRenderer : IRenderer, IScreenshotProvider
}
});
if (ImGuiLayer != null)
{
ImGuiLayer.Render(cmd);
}
Vk.vkCmdEndRenderPass(cmd);
if (_screenshotRequested)
@@ -2,7 +2,7 @@ using System.Runtime.InteropServices;
namespace Engine.Graphics.Vulkan;
internal sealed unsafe class VulkanSwapchain : IDisposable
public sealed unsafe class VulkanSwapchain : IDisposable
{
public VkSwapchainKHR Swapchain;
public VkImage[] SwapchainImages = Array.Empty<VkImage>();
File diff suppressed because it is too large Load Diff