Files
Cortex_Engine/src/Engine.Graphics.Vulkan/VulkanImGui.cs
T
emil28092005 f8384a8672 fix: ImGui font upload barriers — use correct sync2 access flags
0x2000 was VK_ACCESS_MEMORY_READ_BIT in Vulkan 1.0 but maps to
VK_ACCESS_2_HOST_READ_BIT in sync2. Replaced with 0x1000
(VK_ACCESS_2_TRANSFER_WRITE_BIT) for dst access on transfer layout.
2026-06-19 08:54:54 +03:00

727 lines
27 KiB
C#

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, 0x1000);
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, &region);
TransitionImageLayoutCmd(cmd, _fontImage,
VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal,
0x1000, 0x1000, 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);
}
}