feat: ImGui debug overlay — FPS, camera position, entity count
- ImGui.NET 1.91.6.1 integrated into Engine.Graphics.Vulkan - VulkanImGui.cs: font atlas upload (staging → VkImage), sampler, descriptor set (COMBINED_IMAGE_SAMPLER), separate pipeline with alpha blending, no depth write, dynamic scissor - ImGui shaders: imgui.vert (ortho MVP push constant) + imgui.frag (font texture sampler) - Dynamic vertex/index buffers (HOST_VISIBLE, grow on demand) - IRenderer interface: BeginImGuiFrame/EndImGuiFrame default methods - Program.cs: debug window with FPS, camera pos/target, entity count - 0 critical validation errors, ~750 FPS with physics + ImGui
This commit is contained in:
@@ -6,6 +6,7 @@ using Engine.Graphics.Loaders;
|
|||||||
using Engine.Graphics.Vulkan;
|
using Engine.Graphics.Vulkan;
|
||||||
using Engine.Physics;
|
using Engine.Physics;
|
||||||
using Flecs.NET.Core;
|
using Flecs.NET.Core;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
namespace CortexEngine.App;
|
namespace CortexEngine.App;
|
||||||
|
|
||||||
@@ -41,6 +42,13 @@ class Program
|
|||||||
|
|
||||||
CreateScene(world);
|
CreateScene(world);
|
||||||
|
|
||||||
|
var hasImGui = renderer.GetType().Name == "VulkanRenderer";
|
||||||
|
if (hasImGui)
|
||||||
|
{
|
||||||
|
ImGui.GetIO().DisplaySize = new System.Numerics.Vector2(window.Width, window.Height);
|
||||||
|
Console.WriteLine("[App] ImGui initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
var lastWidth = window.Width;
|
var lastWidth = window.Width;
|
||||||
var lastHeight = window.Height;
|
var lastHeight = window.Height;
|
||||||
var frames = 0;
|
var frames = 0;
|
||||||
@@ -82,6 +90,30 @@ class Program
|
|||||||
|
|
||||||
physicsWorld.Update((float)timing.DeltaTime);
|
physicsWorld.Update((float)timing.DeltaTime);
|
||||||
physicsWorld.SyncTransforms(world);
|
physicsWorld.SyncTransforms(world);
|
||||||
|
|
||||||
|
if (hasImGui)
|
||||||
|
{
|
||||||
|
renderer.BeginImGuiFrame();
|
||||||
|
|
||||||
|
ImGui.Begin("Cortex Engine Debug");
|
||||||
|
ImGui.Text($"FPS: {frames}");
|
||||||
|
ImGui.Text($"Delta: {timing.DeltaTime * 1000.0:F2} ms");
|
||||||
|
|
||||||
|
var cam = cameraEntity.Get<Camera>();
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.Text($"Camera Pos: ({cam.Position.X:F2}, {cam.Position.Y:F2}, {cam.Position.Z:F2})");
|
||||||
|
ImGui.Text($"Camera 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("WASD: move | RMB: look | Q/E: up/down | Shift: boost");
|
||||||
|
ImGui.End();
|
||||||
|
|
||||||
|
renderer.EndImGuiFrame();
|
||||||
|
}
|
||||||
|
|
||||||
renderer.RenderWorld(world);
|
renderer.RenderWorld(world);
|
||||||
|
|
||||||
frames++;
|
frames++;
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
<PublishAot>true</PublishAot>
|
<PublishAot>true</PublishAot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ImGui.NET" Version="1.91.6.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
||||||
<ProjectReference Include="..\Engine.Graphics\Engine.Graphics.csproj" />
|
<ProjectReference Include="..\Engine.Graphics\Engine.Graphics.csproj" />
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 fragColor;
|
||||||
|
layout(location = 1) in vec2 fragUV;
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform sampler2D fontTexture;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
outColor = fragColor * texture(fontTexture, fragUV);
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,18 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 inPosition;
|
||||||
|
layout(location = 1) in vec2 inUV;
|
||||||
|
layout(location = 2) in vec4 inColor;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
layout(location = 1) out vec2 fragUV;
|
||||||
|
|
||||||
|
layout(push_constant) uniform PC {
|
||||||
|
mat4 mvp;
|
||||||
|
} pc;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = pc.mvp * vec4(inPosition, 0.0, 1.0);
|
||||||
|
fragColor = inColor;
|
||||||
|
fragUV = inUV;
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -68,10 +68,13 @@ 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 VkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint regionCount, VkBufferImageCopyRegion* pRegions);
|
||||||
public delegate void VkCmdPushConstants(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint offset, uint size, void* pValues);
|
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 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 VkResult VkCreateDescriptorSetLayout(VkDevice device, VkDescriptorSetLayoutCreateInfo* pCreateInfo, nint pAllocator, VkDescriptorSetLayout* pSetLayout);
|
||||||
public delegate void VkDestroyDescriptorSetLayout(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, nint pAllocator);
|
public delegate void VkDestroyDescriptorSetLayout(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, nint pAllocator);
|
||||||
|
public delegate VkResult VkCreateSampler(VkDevice device, VkSamplerCreateInfo* pCreateInfo, nint pAllocator, VkSampler* pSampler);
|
||||||
|
public delegate void VkDestroySampler(VkDevice device, VkSampler sampler, nint pAllocator);
|
||||||
public delegate VkResult VkCreateDescriptorPool(VkDevice device, VkDescriptorPoolCreateInfo* pCreateInfo, nint pAllocator, VkDescriptorPool* pDescriptorPool);
|
public delegate VkResult VkCreateDescriptorPool(VkDevice device, VkDescriptorPoolCreateInfo* pCreateInfo, nint pAllocator, VkDescriptorPool* pDescriptorPool);
|
||||||
public delegate void VkDestroyDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, nint pAllocator);
|
public delegate void VkDestroyDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, nint pAllocator);
|
||||||
public delegate VkResult VkAllocateDescriptorSets(VkDevice device, VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets);
|
public delegate VkResult VkAllocateDescriptorSets(VkDevice device, VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets);
|
||||||
@@ -149,10 +152,13 @@ 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 VkCmdCopyBufferToImage vkCmdCopyBufferToImage;
|
||||||
public static VkCmdPushConstants vkCmdPushConstants;
|
public static VkCmdPushConstants vkCmdPushConstants;
|
||||||
public static VkCmdBindDescriptorSets vkCmdBindDescriptorSets;
|
public static VkCmdBindDescriptorSets vkCmdBindDescriptorSets;
|
||||||
public static VkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
|
public static VkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
|
||||||
public static VkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
|
public static VkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
|
||||||
|
public static VkCreateSampler vkCreateSampler;
|
||||||
|
public static VkDestroySampler vkDestroySampler;
|
||||||
public static VkCreateDescriptorPool vkCreateDescriptorPool;
|
public static VkCreateDescriptorPool vkCreateDescriptorPool;
|
||||||
public static VkDestroyDescriptorPool vkDestroyDescriptorPool;
|
public static VkDestroyDescriptorPool vkDestroyDescriptorPool;
|
||||||
public static VkAllocateDescriptorSets vkAllocateDescriptorSets;
|
public static VkAllocateDescriptorSets vkAllocateDescriptorSets;
|
||||||
@@ -237,10 +243,13 @@ 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");
|
||||||
|
vkCmdCopyBufferToImage = LoadDev<VkCmdCopyBufferToImage>(p, "vkCmdCopyBufferToImage");
|
||||||
vkCmdPushConstants = LoadDev<VkCmdPushConstants>(p, "vkCmdPushConstants");
|
vkCmdPushConstants = LoadDev<VkCmdPushConstants>(p, "vkCmdPushConstants");
|
||||||
vkCmdBindDescriptorSets = LoadDev<VkCmdBindDescriptorSets>(p, "vkCmdBindDescriptorSets");
|
vkCmdBindDescriptorSets = LoadDev<VkCmdBindDescriptorSets>(p, "vkCmdBindDescriptorSets");
|
||||||
vkCreateDescriptorSetLayout = LoadDev<VkCreateDescriptorSetLayout>(p, "vkCreateDescriptorSetLayout");
|
vkCreateDescriptorSetLayout = LoadDev<VkCreateDescriptorSetLayout>(p, "vkCreateDescriptorSetLayout");
|
||||||
vkDestroyDescriptorSetLayout = LoadDev<VkDestroyDescriptorSetLayout>(p, "vkDestroyDescriptorSetLayout");
|
vkDestroyDescriptorSetLayout = LoadDev<VkDestroyDescriptorSetLayout>(p, "vkDestroyDescriptorSetLayout");
|
||||||
|
vkCreateSampler = LoadDev<VkCreateSampler>(p, "vkCreateSampler");
|
||||||
|
vkDestroySampler = LoadDev<VkDestroySampler>(p, "vkDestroySampler");
|
||||||
vkCreateDescriptorPool = LoadDev<VkCreateDescriptorPool>(p, "vkCreateDescriptorPool");
|
vkCreateDescriptorPool = LoadDev<VkCreateDescriptorPool>(p, "vkCreateDescriptorPool");
|
||||||
vkDestroyDescriptorPool = LoadDev<VkDestroyDescriptorPool>(p, "vkDestroyDescriptorPool");
|
vkDestroyDescriptorPool = LoadDev<VkDestroyDescriptorPool>(p, "vkDestroyDescriptorPool");
|
||||||
vkAllocateDescriptorSets = LoadDev<VkAllocateDescriptorSets>(p, "vkAllocateDescriptorSets");
|
vkAllocateDescriptorSets = LoadDev<VkAllocateDescriptorSets>(p, "vkAllocateDescriptorSets");
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public enum VkStructureType : int
|
|||||||
PipelineDynamicStateCreateInfo = 27,
|
PipelineDynamicStateCreateInfo = 27,
|
||||||
GraphicsPipelineCreateInfo = 28,
|
GraphicsPipelineCreateInfo = 28,
|
||||||
PipelineLayoutCreateInfo = 30,
|
PipelineLayoutCreateInfo = 30,
|
||||||
|
SamplerCreateInfo = 31,
|
||||||
RenderPassCreateInfo = 38,
|
RenderPassCreateInfo = 38,
|
||||||
CommandPoolCreateInfo = 39,
|
CommandPoolCreateInfo = 39,
|
||||||
CommandBufferAllocateInfo = 40,
|
CommandBufferAllocateInfo = 40,
|
||||||
@@ -233,6 +234,26 @@ public enum VkImageTiling : int
|
|||||||
Linear = 1,
|
Linear = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum VkFilter : int
|
||||||
|
{
|
||||||
|
Nearest = 0,
|
||||||
|
Linear = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum VkSamplerMipmapMode : int
|
||||||
|
{
|
||||||
|
Nearest = 0,
|
||||||
|
Linear = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum VkSamplerAddressMode : int
|
||||||
|
{
|
||||||
|
Repeat = 0,
|
||||||
|
MirroredRepeat = 1,
|
||||||
|
ClampToEdge = 2,
|
||||||
|
ClampToBorder = 3,
|
||||||
|
}
|
||||||
|
|
||||||
public enum VkBlendFactor : int
|
public enum VkBlendFactor : int
|
||||||
{
|
{
|
||||||
Zero = 0,
|
Zero = 0,
|
||||||
|
|||||||
@@ -44,3 +44,5 @@ public struct VkDescriptorSetLayout { public nint Handle; public static readonly
|
|||||||
public struct VkDescriptorPool { public nint Handle; public static readonly VkDescriptorPool Null = new() { Handle = 0 }; }
|
public struct VkDescriptorPool { public nint Handle; public static readonly VkDescriptorPool Null = new() { Handle = 0 }; }
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct VkDescriptorSet { public nint Handle; public static readonly VkDescriptorSet Null = new() { Handle = 0 }; }
|
public struct VkDescriptorSet { public nint Handle; public static readonly VkDescriptorSet Null = new() { Handle = 0 }; }
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct VkSampler { public nint Handle; public static readonly VkSampler Null = new() { Handle = 0 }; }
|
||||||
|
|||||||
@@ -0,0 +1,726 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace Engine.Graphics.Vulkan;
|
||||||
|
|
||||||
|
internal sealed unsafe class VulkanImGui : IDisposable
|
||||||
|
{
|
||||||
|
private readonly VkDevice _device;
|
||||||
|
private readonly VkPhysicalDevice _physicalDevice;
|
||||||
|
private readonly VkQueue _queue;
|
||||||
|
private readonly VkCommandPool _commandPool;
|
||||||
|
private readonly VulkanContext _ctx;
|
||||||
|
private readonly VkFormat _colorFormat;
|
||||||
|
private readonly VkFormat _depthFormat;
|
||||||
|
|
||||||
|
private VkPipelineLayout _pipelineLayout;
|
||||||
|
private VkPipeline _pipeline;
|
||||||
|
private VkShaderModule _vertModule;
|
||||||
|
private VkShaderModule _fragModule;
|
||||||
|
private VkDescriptorSetLayout _descriptorSetLayout;
|
||||||
|
private VkDescriptorPool _descriptorPool;
|
||||||
|
private VkDescriptorSet _descriptorSet;
|
||||||
|
|
||||||
|
private VkImage _fontImage;
|
||||||
|
private VkDeviceMemory _fontImageMemory;
|
||||||
|
private VkImageView _fontImageView;
|
||||||
|
private VkSampler _fontSampler;
|
||||||
|
|
||||||
|
private VkBuffer _vertexBuffer;
|
||||||
|
private VkDeviceMemory _vertexMemory;
|
||||||
|
private ulong _vertexBufferSize;
|
||||||
|
|
||||||
|
private VkBuffer _indexBuffer;
|
||||||
|
private VkDeviceMemory _indexMemory;
|
||||||
|
private ulong _indexBufferSize;
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public VulkanImGui(VulkanContext ctx, VkCommandPool commandPool, VkFormat colorFormat, VkFormat depthFormat)
|
||||||
|
{
|
||||||
|
_ctx = ctx;
|
||||||
|
_device = ctx.Device;
|
||||||
|
_physicalDevice = ctx.PhysicalDevice;
|
||||||
|
_queue = ctx.GraphicsQueue;
|
||||||
|
_commandPool = commandPool;
|
||||||
|
_colorFormat = colorFormat;
|
||||||
|
_depthFormat = depthFormat;
|
||||||
|
|
||||||
|
var imGuiContext = ImGui.CreateContext();
|
||||||
|
ImGui.SetCurrentContext(imGuiContext);
|
||||||
|
|
||||||
|
var io = ImGui.GetIO();
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard;
|
||||||
|
io.DisplaySize = new System.Numerics.Vector2(1280, 720);
|
||||||
|
|
||||||
|
UploadFontAtlas();
|
||||||
|
CreateDescriptorSetLayout();
|
||||||
|
CreateDescriptorPoolAndSet();
|
||||||
|
CreatePipeline();
|
||||||
|
CreateBuffers();
|
||||||
|
|
||||||
|
Console.WriteLine("[Vulkan] ImGui initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UploadFontAtlas()
|
||||||
|
{
|
||||||
|
var io = ImGui.GetIO();
|
||||||
|
io.Fonts.AddFontDefault();
|
||||||
|
|
||||||
|
io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out int width, out int height);
|
||||||
|
var uploadSize = (ulong)(width * height * 4);
|
||||||
|
|
||||||
|
var stagingBuffer = VkBuffer.Null;
|
||||||
|
var stagingMemory = VkDeviceMemory.Null;
|
||||||
|
CreateBuffer(uploadSize, VkBufferUsageFlags.TransferSrc,
|
||||||
|
VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent,
|
||||||
|
&stagingBuffer, &stagingMemory);
|
||||||
|
|
||||||
|
void* pData = null;
|
||||||
|
Vk.vkMapMemory(_device, stagingMemory, 0, uploadSize, 0, &pData);
|
||||||
|
System.Buffer.MemoryCopy(pixels, pData, (long)uploadSize, (long)uploadSize);
|
||||||
|
Vk.vkUnmapMemory(_device, stagingMemory);
|
||||||
|
|
||||||
|
var imageInfo = new VkImageCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.ImageCreateInfo,
|
||||||
|
imageType = VkImageType.Type2D,
|
||||||
|
format = VkFormat.R8G8B8A8Unorm,
|
||||||
|
extent = new VkExtent3D { Width = (uint)width, Height = (uint)height, Depth = 1 },
|
||||||
|
mipLevels = 1,
|
||||||
|
arrayLayers = 1,
|
||||||
|
samples = VkSampleCountFlags.Count1,
|
||||||
|
tiling = VkImageTiling.Optimal,
|
||||||
|
usage = VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled,
|
||||||
|
sharingMode = VkSharingMode.Exclusive,
|
||||||
|
initialLayout = VkImageLayout.Undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
var img = VkImage.Null;
|
||||||
|
Vk.vkCreateImage(_device, &imageInfo, 0, &img);
|
||||||
|
_fontImage = img;
|
||||||
|
|
||||||
|
var reqs = new VkMemoryRequirements();
|
||||||
|
Vk.vkGetImageMemoryRequirements(_device, _fontImage, &reqs);
|
||||||
|
var memTypeIndex = _ctx.FindMemoryType(reqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal);
|
||||||
|
|
||||||
|
var allocInfo = new VkMemoryAllocateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.MemoryAllocateInfo,
|
||||||
|
allocationSize = reqs.size,
|
||||||
|
memoryTypeIndex = memTypeIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
var fmem = VkDeviceMemory.Null;
|
||||||
|
Vk.vkAllocateMemory(_device, &allocInfo, 0, &fmem);
|
||||||
|
_fontImageMemory = fmem;
|
||||||
|
Vk.vkBindImageMemory(_device, _fontImage, _fontImageMemory, 0);
|
||||||
|
|
||||||
|
var cmd = BeginOneTimeCommands();
|
||||||
|
TransitionImageLayoutCmd(cmd, _fontImage,
|
||||||
|
VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal,
|
||||||
|
0, 0, 0x1000, 0x2000);
|
||||||
|
|
||||||
|
var region = new VkBufferImageCopyRegion
|
||||||
|
{
|
||||||
|
bufferOffset = 0,
|
||||||
|
bufferRowLength = 0,
|
||||||
|
bufferImageHeight = 0,
|
||||||
|
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 = (uint)width, Height = (uint)height, Depth = 1 },
|
||||||
|
};
|
||||||
|
Vk.vkCmdCopyBufferToImage(cmd, stagingBuffer, _fontImage, VkImageLayout.TransferDstOptimal, 1, ®ion);
|
||||||
|
|
||||||
|
TransitionImageLayoutCmd(cmd, _fontImage,
|
||||||
|
VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal,
|
||||||
|
0x1000, 0x2000, 0x8, 0x20);
|
||||||
|
|
||||||
|
EndOneTimeCommands(cmd);
|
||||||
|
|
||||||
|
Vk.vkDestroyBuffer(_device, stagingBuffer, 0);
|
||||||
|
Vk.vkFreeMemory(_device, stagingMemory, 0);
|
||||||
|
|
||||||
|
var viewInfo = new VkImageViewCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.ImageViewCreateInfo,
|
||||||
|
image = _fontImage,
|
||||||
|
viewType = VkImageViewType.Type2D,
|
||||||
|
format = VkFormat.R8G8B8A8Unorm,
|
||||||
|
components = new VkComponentMapping { R = VkComponentSwizzle.Identity, G = VkComponentSwizzle.Identity, B = VkComponentSwizzle.Identity, A = VkComponentSwizzle.Identity },
|
||||||
|
subresourceRange = new VkImageSubresourceRange { AspectMask = VkImageAspectFlags.Color, BaseMipLevel = 0, LevelCount = 1, BaseArrayLayer = 0, LayerCount = 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var fview = VkImageView.Null;
|
||||||
|
Vk.vkCreateImageView(_device, &viewInfo, 0, &fview);
|
||||||
|
_fontImageView = fview;
|
||||||
|
|
||||||
|
var samplerInfo = new VkSamplerCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.SamplerCreateInfo,
|
||||||
|
magFilter = VkFilter.Linear,
|
||||||
|
minFilter = VkFilter.Linear,
|
||||||
|
mipmapMode = VkSamplerMipmapMode.Linear,
|
||||||
|
addressModeU = VkSamplerAddressMode.Repeat,
|
||||||
|
addressModeV = VkSamplerAddressMode.Repeat,
|
||||||
|
addressModeW = VkSamplerAddressMode.Repeat,
|
||||||
|
minLod = -1000,
|
||||||
|
maxLod = 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
var fsamp = VkSampler.Null;
|
||||||
|
Vk.vkCreateSampler(_device, &samplerInfo, 0, &fsamp);
|
||||||
|
_fontSampler = fsamp;
|
||||||
|
|
||||||
|
io.Fonts.SetTexID((nint)1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateDescriptorSetLayout()
|
||||||
|
{
|
||||||
|
var binding = new VkDescriptorSetLayoutBinding
|
||||||
|
{
|
||||||
|
binding = 0,
|
||||||
|
descriptorType = VkDescriptorType.CombinedImageSampler,
|
||||||
|
descriptorCount = 1,
|
||||||
|
stageFlags = VkShaderStageFlags.Fragment,
|
||||||
|
};
|
||||||
|
|
||||||
|
var info = new VkDescriptorSetLayoutCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.DescriptorSetLayoutCreateInfo,
|
||||||
|
bindingCount = 1,
|
||||||
|
pBindings = &binding,
|
||||||
|
};
|
||||||
|
|
||||||
|
var layout = VkDescriptorSetLayout.Null;
|
||||||
|
Vk.vkCreateDescriptorSetLayout(_device, &info, 0, &layout);
|
||||||
|
_descriptorSetLayout = layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateDescriptorPoolAndSet()
|
||||||
|
{
|
||||||
|
var poolSize = new VkDescriptorPoolSize
|
||||||
|
{
|
||||||
|
type = VkDescriptorType.CombinedImageSampler,
|
||||||
|
descriptorCount = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
var poolInfo = new VkDescriptorPoolCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.DescriptorPoolCreateInfo,
|
||||||
|
maxSets = 1,
|
||||||
|
poolSizeCount = 1,
|
||||||
|
pPoolSizes = &poolSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pool = VkDescriptorPool.Null;
|
||||||
|
Vk.vkCreateDescriptorPool(_device, &poolInfo, 0, &pool);
|
||||||
|
_descriptorPool = pool;
|
||||||
|
|
||||||
|
var layout = _descriptorSetLayout;
|
||||||
|
var allocInfo = new VkDescriptorSetAllocateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.DescriptorSetAllocateInfo,
|
||||||
|
descriptorPool = _descriptorPool,
|
||||||
|
descriptorSetCount = 1,
|
||||||
|
pSetLayouts = &layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
var set = VkDescriptorSet.Null;
|
||||||
|
Vk.vkAllocateDescriptorSets(_device, &allocInfo, &set);
|
||||||
|
_descriptorSet = set;
|
||||||
|
|
||||||
|
var imageInfo = new VkDescriptorImageInfo
|
||||||
|
{
|
||||||
|
sampler = _fontSampler,
|
||||||
|
imageView = _fontImageView,
|
||||||
|
imageLayout = VkImageLayout.ShaderReadOnlyOptimal,
|
||||||
|
};
|
||||||
|
|
||||||
|
var write = new VkWriteDescriptorSet
|
||||||
|
{
|
||||||
|
sType = VkStructureType.WriteDescriptorSet,
|
||||||
|
dstSet = _descriptorSet,
|
||||||
|
dstBinding = 0,
|
||||||
|
descriptorCount = 1,
|
||||||
|
descriptorType = VkDescriptorType.CombinedImageSampler,
|
||||||
|
pImageInfo = (nint)(&imageInfo),
|
||||||
|
};
|
||||||
|
|
||||||
|
Vk.vkUpdateDescriptorSets(_device, 1, &write, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreatePipeline()
|
||||||
|
{
|
||||||
|
_vertModule = CreateShaderModule(LoadShader("Shaders/imgui.vert.spv"));
|
||||||
|
_fragModule = CreateShaderModule(LoadShader("Shaders/imgui.frag.spv"));
|
||||||
|
|
||||||
|
fixed (byte* pName = "main\0"u8)
|
||||||
|
{
|
||||||
|
var stages = stackalloc VkPipelineShaderStageCreateInfo[2];
|
||||||
|
stages[0] = new VkPipelineShaderStageCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineShaderStageCreateInfo,
|
||||||
|
stage = VkShaderStageFlags.Vertex,
|
||||||
|
module = _vertModule,
|
||||||
|
pName = pName,
|
||||||
|
};
|
||||||
|
stages[1] = new VkPipelineShaderStageCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineShaderStageCreateInfo,
|
||||||
|
stage = VkShaderStageFlags.Fragment,
|
||||||
|
module = _fragModule,
|
||||||
|
pName = pName,
|
||||||
|
};
|
||||||
|
|
||||||
|
var bindings = stackalloc VkVertexInputBindingDescription[1];
|
||||||
|
bindings[0] = new VkVertexInputBindingDescription
|
||||||
|
{
|
||||||
|
binding = 0,
|
||||||
|
stride = 20,
|
||||||
|
inputRate = VkVertexInputRate.Vertex,
|
||||||
|
};
|
||||||
|
|
||||||
|
var attributes = stackalloc VkVertexInputAttributeDescription[3];
|
||||||
|
attributes[0] = new VkVertexInputAttributeDescription { location = 0, binding = 0, format = VkFormat.R32G32Sfloat, offset = 0 };
|
||||||
|
attributes[1] = new VkVertexInputAttributeDescription { location = 1, binding = 0, format = VkFormat.R32G32Sfloat, offset = 8 };
|
||||||
|
attributes[2] = new VkVertexInputAttributeDescription { location = 2, binding = 0, format = VkFormat.R8G8B8A8Unorm, offset = 16 };
|
||||||
|
|
||||||
|
var vertexInputState = new VkPipelineVertexInputStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineVertexInputStateCreateInfo,
|
||||||
|
vertexBindingDescriptionCount = 1,
|
||||||
|
pVertexBindingDescriptions = bindings,
|
||||||
|
vertexAttributeDescriptionCount = 3,
|
||||||
|
pVertexAttributeDescriptions = attributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
var inputAssemblyState = new VkPipelineInputAssemblyStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineInputAssemblyStateCreateInfo,
|
||||||
|
topology = VkPrimitiveTopology.TriangleList,
|
||||||
|
primitiveRestartEnable = VkBool32.False,
|
||||||
|
};
|
||||||
|
|
||||||
|
var viewportState = new VkPipelineViewportStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineViewportStateCreateInfo,
|
||||||
|
viewportCount = 1,
|
||||||
|
scissorCount = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
var rasterizationState = new VkPipelineRasterizationStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineRasterizationStateCreateInfo,
|
||||||
|
polygonMode = VkPolygonMode.Fill,
|
||||||
|
cullMode = VkCullModeFlags.None,
|
||||||
|
frontFace = VkFrontFace.CounterClockwise,
|
||||||
|
lineWidth = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
var multisampleState = new VkPipelineMultisampleStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineMultisampleStateCreateInfo,
|
||||||
|
rasterizationSamples = VkSampleCountFlags.Count1,
|
||||||
|
};
|
||||||
|
|
||||||
|
var depthStencilState = new VkPipelineDepthStencilStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineDepthStencilStateCreateInfo,
|
||||||
|
depthTestEnable = VkBool32.False,
|
||||||
|
depthWriteEnable = VkBool32.False,
|
||||||
|
};
|
||||||
|
|
||||||
|
var blendAttachment = new VkPipelineColorBlendAttachmentState
|
||||||
|
{
|
||||||
|
blendEnable = VkBool32.True,
|
||||||
|
srcColorBlendFactor = VkBlendFactor.SrcAlpha,
|
||||||
|
dstColorBlendFactor = VkBlendFactor.OneMinusSrcAlpha,
|
||||||
|
colorBlendOp = VkBlendOp.Add,
|
||||||
|
srcAlphaBlendFactor = VkBlendFactor.One,
|
||||||
|
dstAlphaBlendFactor = VkBlendFactor.OneMinusSrcAlpha,
|
||||||
|
alphaBlendOp = VkBlendOp.Add,
|
||||||
|
colorWriteMask = VkColorComponentFlags.R | VkColorComponentFlags.G | VkColorComponentFlags.B | VkColorComponentFlags.A,
|
||||||
|
};
|
||||||
|
|
||||||
|
var colorBlendState = new VkPipelineColorBlendStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineColorBlendStateCreateInfo,
|
||||||
|
logicOpEnable = VkBool32.False,
|
||||||
|
attachmentCount = 1,
|
||||||
|
pAttachments = &blendAttachment,
|
||||||
|
};
|
||||||
|
|
||||||
|
var dynamicStates = stackalloc VkDynamicState[2];
|
||||||
|
dynamicStates[0] = VkDynamicState.Viewport;
|
||||||
|
dynamicStates[1] = VkDynamicState.Scissor;
|
||||||
|
|
||||||
|
var dynamicState = new VkPipelineDynamicStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineDynamicStateCreateInfo,
|
||||||
|
dynamicStateCount = 2,
|
||||||
|
pDynamicStates = dynamicStates,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pushConstantRange = new VkPushConstantRange
|
||||||
|
{
|
||||||
|
stageFlags = VkShaderStageFlags.Vertex,
|
||||||
|
offset = 0,
|
||||||
|
size = 64,
|
||||||
|
};
|
||||||
|
|
||||||
|
var descLayout = _descriptorSetLayout;
|
||||||
|
var layoutInfo = new VkPipelineLayoutCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineLayoutCreateInfo,
|
||||||
|
setLayoutCount = 1,
|
||||||
|
pSetLayouts = &descLayout,
|
||||||
|
pushConstantRangeCount = 1,
|
||||||
|
pPushConstantRanges = (nint)(&pushConstantRange),
|
||||||
|
};
|
||||||
|
|
||||||
|
var pl = VkPipelineLayout.Null;
|
||||||
|
Vk.vkCreatePipelineLayout(_device, &layoutInfo, 0, &pl);
|
||||||
|
_pipelineLayout = pl;
|
||||||
|
|
||||||
|
var cf = _colorFormat;
|
||||||
|
var df = _depthFormat;
|
||||||
|
var renderingInfo = new VkPipelineRenderingCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineRenderingCreateInfo,
|
||||||
|
colorAttachmentCount = 1,
|
||||||
|
pColorAttachmentFormats = &cf,
|
||||||
|
depthAttachmentFormat = df,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pipelineInfo = new VkGraphicsPipelineCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.GraphicsPipelineCreateInfo,
|
||||||
|
pNext = (nint)(&renderingInfo),
|
||||||
|
stageCount = 2,
|
||||||
|
pStages = stages,
|
||||||
|
pVertexInputState = &vertexInputState,
|
||||||
|
pInputAssemblyState = &inputAssemblyState,
|
||||||
|
pViewportState = &viewportState,
|
||||||
|
pRasterizationState = &rasterizationState,
|
||||||
|
pMultisampleState = &multisampleState,
|
||||||
|
pDepthStencilState = &depthStencilState,
|
||||||
|
pColorBlendState = &colorBlendState,
|
||||||
|
pDynamicState = &dynamicState,
|
||||||
|
layout = _pipelineLayout,
|
||||||
|
renderPass = new VkRenderPass { Handle = 0 },
|
||||||
|
subpass = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pp = VkPipeline.Null;
|
||||||
|
Vk.vkCreateGraphicsPipelines(_device, 0, 1, &pipelineInfo, 0, &pp);
|
||||||
|
_pipeline = pp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateBuffers()
|
||||||
|
{
|
||||||
|
_vertexBufferSize = 65536;
|
||||||
|
_indexBufferSize = 65536 * 2;
|
||||||
|
|
||||||
|
var vb = VkBuffer.Null;
|
||||||
|
var vm = VkDeviceMemory.Null;
|
||||||
|
CreateBuffer(_vertexBufferSize, VkBufferUsageFlags.VertexBuffer,
|
||||||
|
VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent,
|
||||||
|
&vb, &vm);
|
||||||
|
_vertexBuffer = vb;
|
||||||
|
_vertexMemory = vm;
|
||||||
|
|
||||||
|
var ib = VkBuffer.Null;
|
||||||
|
var im = VkDeviceMemory.Null;
|
||||||
|
CreateBuffer(_indexBufferSize, VkBufferUsageFlags.IndexBuffer,
|
||||||
|
VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent,
|
||||||
|
&ib, &im);
|
||||||
|
_indexBuffer = ib;
|
||||||
|
_indexMemory = im;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NewFrame()
|
||||||
|
{
|
||||||
|
var io = ImGui.GetIO();
|
||||||
|
io.DisplaySize = new System.Numerics.Vector2(1280, 720);
|
||||||
|
io.DeltaTime = 0.016f;
|
||||||
|
ImGui.NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render(VkCommandBuffer cmd, uint width, uint height)
|
||||||
|
{
|
||||||
|
var drawData = ImGui.GetDrawData();
|
||||||
|
if (drawData.CmdListsCount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var io = ImGui.GetIO();
|
||||||
|
var scale = Matrix4x4.CreateScale(2f / io.DisplaySize.X, 2f / io.DisplaySize.Y, 1f);
|
||||||
|
var trans = Matrix4x4.CreateTranslation(-1f, -1f, 0f);
|
||||||
|
var mvp = scale * trans;
|
||||||
|
|
||||||
|
EnsureBufferSize(ref _vertexBuffer, ref _vertexMemory, ref _vertexBufferSize,
|
||||||
|
(ulong)(drawData.TotalVtxCount * 20), VkBufferUsageFlags.VertexBuffer);
|
||||||
|
EnsureBufferSize(ref _indexBuffer, ref _indexMemory, ref _indexBufferSize,
|
||||||
|
(ulong)(drawData.TotalIdxCount * 2), VkBufferUsageFlags.IndexBuffer);
|
||||||
|
|
||||||
|
void* pVtx = null;
|
||||||
|
var totalVtxSize = (ulong)(drawData.TotalVtxCount * 20);
|
||||||
|
Vk.vkMapMemory(_device, _vertexMemory, 0, totalVtxSize, 0, &pVtx);
|
||||||
|
void* pIdx = null;
|
||||||
|
var totalIdxSize = (ulong)(drawData.TotalIdxCount * 2);
|
||||||
|
Vk.vkMapMemory(_device, _indexMemory, 0, totalIdxSize, 0, &pIdx);
|
||||||
|
|
||||||
|
var vtxPtr = (byte*)pVtx;
|
||||||
|
var idxPtr = (byte*)pIdx;
|
||||||
|
for (int n = 0; n < drawData.CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
var list = drawData.CmdLists[n];
|
||||||
|
var vtxBytes = list.VtxBuffer.Size * 20;
|
||||||
|
System.Buffer.MemoryCopy((void*)list.VtxBuffer.Data, vtxPtr, vtxBytes, vtxBytes);
|
||||||
|
vtxPtr += vtxBytes;
|
||||||
|
|
||||||
|
var idxBytes = list.IdxBuffer.Size * 2;
|
||||||
|
System.Buffer.MemoryCopy((void*)list.IdxBuffer.Data, idxPtr, idxBytes, idxBytes);
|
||||||
|
idxPtr += idxBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vk.vkUnmapMemory(_device, _vertexMemory);
|
||||||
|
Vk.vkUnmapMemory(_device, _indexMemory);
|
||||||
|
|
||||||
|
Vk.vkCmdBindPipeline(cmd, VkPipelineBindPoint.Graphics, _pipeline);
|
||||||
|
|
||||||
|
var vp = new VkViewport { X = 0, Y = 0, Width = width, Height = height, MinDepth = 0, MaxDepth = 1 };
|
||||||
|
Vk.vkCmdSetViewport(cmd, 0, 1, &vp);
|
||||||
|
|
||||||
|
var descSet = _descriptorSet;
|
||||||
|
Vk.vkCmdBindDescriptorSets(cmd, VkPipelineBindPoint.Graphics, _pipelineLayout,
|
||||||
|
0, 1, &descSet, 0, null);
|
||||||
|
|
||||||
|
var vb = _vertexBuffer;
|
||||||
|
ulong voff = 0;
|
||||||
|
Vk.vkCmdBindVertexBuffers(cmd, 0, 1, &vb, &voff);
|
||||||
|
Vk.vkCmdBindIndexBuffer(cmd, _indexBuffer, 0, 0);
|
||||||
|
|
||||||
|
Vk.vkCmdPushConstants(cmd, _pipelineLayout, VkShaderStageFlags.Vertex, 0, 64, &mvp);
|
||||||
|
|
||||||
|
int vtxOffset = 0;
|
||||||
|
int idxOffset = 0;
|
||||||
|
for (int n = 0; n < drawData.CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
var list = drawData.CmdLists[n];
|
||||||
|
for (int i = 0; i < list.CmdBuffer.Size; i++)
|
||||||
|
{
|
||||||
|
var pcmd = list.CmdBuffer[i];
|
||||||
|
var clip = pcmd.ClipRect;
|
||||||
|
var scissor = new VkRect2D
|
||||||
|
{
|
||||||
|
Offset = new VkOffset2D { X = (int)clip.X, Y = (int)clip.Y },
|
||||||
|
Extent = new VkExtent2D
|
||||||
|
{
|
||||||
|
Width = (uint)(clip.Z - clip.X),
|
||||||
|
Height = (uint)(clip.W - clip.Y),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Vk.vkCmdSetScissor(cmd, 0, 1, &scissor);
|
||||||
|
Vk.vkCmdDrawIndexed(cmd, pcmd.ElemCount, 1, (uint)(idxOffset + pcmd.IdxOffset), (int)(vtxOffset + pcmd.VtxOffset), 0);
|
||||||
|
}
|
||||||
|
vtxOffset += list.VtxBuffer.Size;
|
||||||
|
idxOffset += list.IdxBuffer.Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureBufferSize(ref VkBuffer buffer, ref VkDeviceMemory memory, ref ulong currentSize,
|
||||||
|
ulong neededSize, VkBufferUsageFlags usage)
|
||||||
|
{
|
||||||
|
if (neededSize <= currentSize) return;
|
||||||
|
GrowBuffer(ref buffer, ref memory, ref currentSize, neededSize, usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateBuffer(ulong size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props,
|
||||||
|
VkBuffer* pBuffer, VkDeviceMemory* pMemory)
|
||||||
|
{
|
||||||
|
var info = new VkBufferCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.BufferCreateInfo,
|
||||||
|
size = size,
|
||||||
|
usage = usage,
|
||||||
|
sharingMode = VkSharingMode.Exclusive,
|
||||||
|
};
|
||||||
|
|
||||||
|
Vk.vkCreateBuffer(_device, &info, 0, pBuffer);
|
||||||
|
|
||||||
|
var reqs = new VkMemoryRequirements();
|
||||||
|
Vk.vkGetBufferMemoryRequirements(_device, *pBuffer, &reqs);
|
||||||
|
|
||||||
|
var memTypeIndex = _ctx.FindMemoryType(reqs.memoryTypeBits, props);
|
||||||
|
|
||||||
|
var allocInfo = new VkMemoryAllocateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.MemoryAllocateInfo,
|
||||||
|
allocationSize = reqs.size,
|
||||||
|
memoryTypeIndex = memTypeIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
Vk.vkAllocateMemory(_device, &allocInfo, 0, pMemory);
|
||||||
|
Vk.vkBindBufferMemory(_device, *pBuffer, *pMemory, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GrowBuffer(ref VkBuffer buffer, ref VkDeviceMemory memory, ref ulong currentSize,
|
||||||
|
ulong neededSize, VkBufferUsageFlags usage)
|
||||||
|
{
|
||||||
|
Vk.vkDeviceWaitIdle(_device);
|
||||||
|
|
||||||
|
if (buffer.Handle != 0) Vk.vkDestroyBuffer(_device, buffer, 0);
|
||||||
|
if (memory.Handle != 0) Vk.vkFreeMemory(_device, memory, 0);
|
||||||
|
|
||||||
|
currentSize = neededSize * 2;
|
||||||
|
var b = VkBuffer.Null;
|
||||||
|
var m = VkDeviceMemory.Null;
|
||||||
|
CreateBuffer(currentSize, usage,
|
||||||
|
VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent,
|
||||||
|
&b, &m);
|
||||||
|
buffer = b;
|
||||||
|
memory = m;
|
||||||
|
}
|
||||||
|
private VkShaderModule CreateShaderModule(byte[] spv)
|
||||||
|
{
|
||||||
|
fixed (byte* pCode = spv)
|
||||||
|
{
|
||||||
|
var info = new VkShaderModuleCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.ShaderModuleCreateInfo,
|
||||||
|
codeSize = (nuint)spv.Length,
|
||||||
|
pCode = (uint*)pCode,
|
||||||
|
};
|
||||||
|
|
||||||
|
var module = VkShaderModule.Null;
|
||||||
|
Vk.vkCreateShaderModule(_device, &info, 0, &module);
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private VkCommandBuffer BeginOneTimeCommands()
|
||||||
|
{
|
||||||
|
var allocInfo = new VkCommandBufferAllocateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.CommandBufferAllocateInfo,
|
||||||
|
commandPool = _commandPool,
|
||||||
|
level = VkCommandBufferLevel.Primary,
|
||||||
|
commandBufferCount = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
var cmd = VkCommandBuffer.Null;
|
||||||
|
Vk.vkAllocateCommandBuffers(_device, &allocInfo, &cmd);
|
||||||
|
|
||||||
|
var beginInfo = new VkCommandBufferBeginInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.CommandBufferBeginInfo,
|
||||||
|
flags = VkCommandBufferUsageFlags.OneTimeSubmit,
|
||||||
|
};
|
||||||
|
|
||||||
|
Vk.vkBeginCommandBuffer(cmd, &beginInfo);
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndOneTimeCommands(VkCommandBuffer cmd)
|
||||||
|
{
|
||||||
|
Vk.vkEndCommandBuffer(cmd);
|
||||||
|
|
||||||
|
var cmdInfo = new VkCommandBufferSubmitInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.CommandBufferSubmitInfo,
|
||||||
|
commandBuffer = cmd,
|
||||||
|
};
|
||||||
|
|
||||||
|
var submitInfo = new VkSubmitInfo2
|
||||||
|
{
|
||||||
|
sType = VkStructureType.SubmitInfo2,
|
||||||
|
commandBufferInfoCount = 1,
|
||||||
|
pCommandBufferInfos = &cmdInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
Vk.vkQueueSubmit2(_queue, 1, &submitInfo, VkFence.Null);
|
||||||
|
Vk.vkQueueWaitIdle(_queue);
|
||||||
|
|
||||||
|
Vk.vkFreeCommandBuffers(_device, _commandPool, 1, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TransitionImageLayoutCmd(VkCommandBuffer cmd, VkImage image,
|
||||||
|
VkImageLayout oldLayout, VkImageLayout newLayout,
|
||||||
|
ulong srcStage, ulong srcAccess, ulong dstStage, ulong dstAccess)
|
||||||
|
{
|
||||||
|
var barrier = new VkImageMemoryBarrier2
|
||||||
|
{
|
||||||
|
sType = VkStructureType.ImageMemoryBarrier2,
|
||||||
|
srcStageMask = srcStage,
|
||||||
|
srcAccessMask = srcAccess,
|
||||||
|
dstStageMask = dstStage,
|
||||||
|
dstAccessMask = dstAccess,
|
||||||
|
oldLayout = oldLayout,
|
||||||
|
newLayout = newLayout,
|
||||||
|
image = image,
|
||||||
|
subresourceRange = new VkImageSubresourceRange
|
||||||
|
{
|
||||||
|
AspectMask = VkImageAspectFlags.Color,
|
||||||
|
LevelCount = 1,
|
||||||
|
LayerCount = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var depInfo = new VkDependencyInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.DependencyInfo,
|
||||||
|
imageMemoryBarrierCount = 1,
|
||||||
|
pImageMemoryBarriers = &barrier,
|
||||||
|
};
|
||||||
|
|
||||||
|
Vk.vkCmdPipelineBarrier2(cmd, &depInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] LoadShader(string path)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
var altPath = Path.Combine(AppContext.BaseDirectory, path);
|
||||||
|
if (!File.Exists(altPath))
|
||||||
|
{
|
||||||
|
altPath = Path.Combine(AppContext.BaseDirectory, "Shaders", Path.GetFileName(path));
|
||||||
|
if (!File.Exists(altPath))
|
||||||
|
throw new FileNotFoundException($"Shader file not found: {path}");
|
||||||
|
}
|
||||||
|
return File.ReadAllBytes(altPath);
|
||||||
|
}
|
||||||
|
return File.ReadAllBytes(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
Vk.vkDeviceWaitIdle(_device);
|
||||||
|
|
||||||
|
if (_pipeline.Handle != 0) Vk.vkDestroyPipeline(_device, _pipeline, 0);
|
||||||
|
if (_pipelineLayout.Handle != 0) Vk.vkDestroyPipelineLayout(_device, _pipelineLayout, 0);
|
||||||
|
if (_descriptorPool.Handle != 0) Vk.vkDestroyDescriptorPool(_device, _descriptorPool, 0);
|
||||||
|
if (_descriptorSetLayout.Handle != 0) Vk.vkDestroyDescriptorSetLayout(_device, _descriptorSetLayout, 0);
|
||||||
|
if (_fontSampler.Handle != 0) Vk.vkDestroySampler(_device, _fontSampler, 0);
|
||||||
|
if (_fontImageView.Handle != 0) Vk.vkDestroyImageView(_device, _fontImageView, 0);
|
||||||
|
if (_fontImage.Handle != 0) Vk.vkDestroyImage(_device, _fontImage, 0);
|
||||||
|
if (_fontImageMemory.Handle != 0) Vk.vkFreeMemory(_device, _fontImageMemory, 0);
|
||||||
|
if (_vertexBuffer.Handle != 0) Vk.vkDestroyBuffer(_device, _vertexBuffer, 0);
|
||||||
|
if (_vertexMemory.Handle != 0) Vk.vkFreeMemory(_device, _vertexMemory, 0);
|
||||||
|
if (_indexBuffer.Handle != 0) Vk.vkDestroyBuffer(_device, _indexBuffer, 0);
|
||||||
|
if (_indexMemory.Handle != 0) Vk.vkFreeMemory(_device, _indexMemory, 0);
|
||||||
|
if (_vertModule.Handle != 0) Vk.vkDestroyShaderModule(_device, _vertModule, 0);
|
||||||
|
if (_fragModule.Handle != 0) Vk.vkDestroyShaderModule(_device, _fragModule, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,7 @@ using Engine.Core;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Vulkan;
|
namespace Engine.Graphics.Vulkan;
|
||||||
|
|
||||||
internal sealed class VulkanRenderContext : IRenderContext
|
internal sealed class VulkanRenderContext : IRenderContext{
|
||||||
{
|
|
||||||
private readonly VulkanContext _ctx;
|
private readonly VulkanContext _ctx;
|
||||||
private readonly VulkanSwapchain _swapchain;
|
private readonly VulkanSwapchain _swapchain;
|
||||||
private readonly IWindow _window;
|
private readonly IWindow _window;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
private readonly VulkanSwapchain _swapchain;
|
private readonly VulkanSwapchain _swapchain;
|
||||||
private readonly VulkanPipeline _pipeline;
|
private readonly VulkanPipeline _pipeline;
|
||||||
private readonly VulkanFrameResources _frameResources;
|
private readonly VulkanFrameResources _frameResources;
|
||||||
|
internal readonly VulkanImGui? _imGui;
|
||||||
|
|
||||||
private readonly Dictionary<ulong, (VulkanVertexBuffer vb, VulkanIndexBuffer ib, uint indexCount)> _meshCache = new();
|
private readonly Dictionary<ulong, (VulkanVertexBuffer vb, VulkanIndexBuffer ib, uint indexCount)> _meshCache = new();
|
||||||
|
|
||||||
@@ -37,6 +38,21 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
|
|
||||||
_frameResources = new VulkanFrameResources(ctx.Device, ctx.GraphicsQueueFamilyIndex,
|
_frameResources = new VulkanFrameResources(ctx.Device, ctx.GraphicsQueueFamilyIndex,
|
||||||
swapchain.ImageCount, ctx, _pipeline.DescriptorSetLayout);
|
swapchain.ImageCount, ctx, _pipeline.DescriptorSetLayout);
|
||||||
|
|
||||||
|
_imGui = new VulkanImGui(ctx, _frameResources.CommandPool, swapchain.Format, swapchain.DepthFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal VulkanImGui? ImGuiLayer => _imGui;
|
||||||
|
|
||||||
|
public void BeginImGuiFrame()
|
||||||
|
{
|
||||||
|
_imGui?.NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndImGuiFrame()
|
||||||
|
{
|
||||||
|
if (_imGui == null) return;
|
||||||
|
ImGuiNET.ImGui.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RenderWorld(World world)
|
public void RenderWorld(World world)
|
||||||
@@ -205,6 +221,8 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
Vk.vkCmdDrawIndexed(cmd, dc.indexCount, 1, 0, 0, 0);
|
Vk.vkCmdDrawIndexed(cmd, dc.indexCount, 1, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_imGui?.Render(cmd, _swapchain.Extent.Width, _swapchain.Extent.Height);
|
||||||
|
|
||||||
Vk.vkCmdEndRendering(cmd);
|
Vk.vkCmdEndRendering(cmd);
|
||||||
|
|
||||||
TransitionImageLayout(cmd, _swapchain.Images[imageIndex],
|
TransitionImageLayout(cmd, _swapchain.Images[imageIndex],
|
||||||
@@ -389,6 +407,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
}
|
}
|
||||||
_meshCache.Clear();
|
_meshCache.Clear();
|
||||||
|
|
||||||
|
_imGui?.Dispose();
|
||||||
_frameResources?.Dispose();
|
_frameResources?.Dispose();
|
||||||
_pipeline?.Dispose();
|
_pipeline?.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -957,6 +957,57 @@ public unsafe struct VkImageCreateInfo
|
|||||||
public VkImageLayout initialLayout;
|
public VkImageLayout initialLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkImageSubresourceLayers
|
||||||
|
{
|
||||||
|
public VkImageAspectFlags aspectMask;
|
||||||
|
public uint mipLevel;
|
||||||
|
public uint baseArrayLayer;
|
||||||
|
public uint layerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkBufferImageCopyRegion
|
||||||
|
{
|
||||||
|
public ulong bufferOffset;
|
||||||
|
public uint bufferRowLength;
|
||||||
|
public uint bufferImageHeight;
|
||||||
|
public VkImageSubresourceLayers imageSubresource;
|
||||||
|
public VkOffset3D imageOffset;
|
||||||
|
public VkExtent3D imageExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkSamplerCreateInfo
|
||||||
|
{
|
||||||
|
public VkStructureType sType;
|
||||||
|
public nint pNext;
|
||||||
|
public uint flags;
|
||||||
|
public VkFilter magFilter;
|
||||||
|
public VkFilter minFilter;
|
||||||
|
public VkSamplerMipmapMode mipmapMode;
|
||||||
|
public VkSamplerAddressMode addressModeU;
|
||||||
|
public VkSamplerAddressMode addressModeV;
|
||||||
|
public VkSamplerAddressMode addressModeW;
|
||||||
|
public float mipLodBias;
|
||||||
|
public VkBool32 anisotropyEnable;
|
||||||
|
public float maxAnisotropy;
|
||||||
|
public VkBool32 compareEnable;
|
||||||
|
public VkCompareOp compareOp;
|
||||||
|
public float minLod;
|
||||||
|
public float maxLod;
|
||||||
|
public int borderColor;
|
||||||
|
public VkBool32 unnormalizedCoordinates;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct VkDescriptorImageInfo
|
||||||
|
{
|
||||||
|
public VkSampler sampler;
|
||||||
|
public VkImageView imageView;
|
||||||
|
public VkImageLayout imageLayout;
|
||||||
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public unsafe struct VkPushConstantRange
|
public unsafe struct VkPushConstantRange
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,4 +13,16 @@ public interface IRenderer : IDisposable
|
|||||||
void RequestScreenshot(string path);
|
void RequestScreenshot(string path);
|
||||||
bool IsScreenshotRequested { get; }
|
bool IsScreenshotRequested { get; }
|
||||||
IScreenshotProvider ScreenshotProvider { get; }
|
IScreenshotProvider ScreenshotProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called before RenderWorld to begin a new ImGui frame.
|
||||||
|
/// Null if the backend doesn't support ImGui.
|
||||||
|
/// </summary>
|
||||||
|
void BeginImGuiFrame() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after RenderWorld to render ImGui draw data.
|
||||||
|
/// Null if the backend doesn't support ImGui.
|
||||||
|
/// </summary>
|
||||||
|
void EndImGuiFrame() { }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user