From d6bd1807cf3da44d47abe1a55df6004764060e80 Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Thu, 18 Jun 2026 02:14:54 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20depth=20buffer=20+=203D=20model=20matri?= =?UTF-8?q?x=20=E2=80=94=20rotating=20cube=20now=20looks=20correct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Depth image (D32_SFLOAT) + image view created in VulkanSwapchain - VkPipelineDepthStencilStateCreateInfo: depth test + write enabled, CompareOp=Less - Push constant changed from float angle (4B) to mat4 model (64B) - 3D rotation: RotateY * RotateX in renderer, applied via push constant - Vertex shader: gl_Position = vp * model * vec4(pos, 1.0) - Depth attachment in VkRenderingInfo + depth clear (1.0) - Depth image layout transition (Undefined → DepthStencilAttachmentOptimal) - New Vulkan functions: vkCreateImage, vkDestroyImage, vkGetImageMemoryRequirements, vkBindImageMemory - New structs: VkPipelineDepthStencilStateCreateInfo, VkStencilOpState, VkImageCreateInfo - New enums: VkCompareOp, VkImageType, VkImageTiling - 0 validation errors --- .../Shaders/triangle.vert | 13 +-- .../Shaders/triangle.vert.spv | Bin 2164 -> 1544 bytes src/Engine.Graphics.Vulkan/Vk.cs | 12 +++ src/Engine.Graphics.Vulkan/VulkanEnums.cs | 28 +++++ src/Engine.Graphics.Vulkan/VulkanPipeline.cs | 16 ++- .../VulkanRenderContext.cs | 2 +- src/Engine.Graphics.Vulkan/VulkanRenderer.cs | 59 ++++++++++- src/Engine.Graphics.Vulkan/VulkanStructs.cs | 53 +++++++++- src/Engine.Graphics.Vulkan/VulkanSwapchain.cs | 100 +++++++++++++++++- 9 files changed, 265 insertions(+), 18 deletions(-) diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.vert b/src/Engine.Graphics.Vulkan/Shaders/triangle.vert index 7519551..d587a87 100644 --- a/src/Engine.Graphics.Vulkan/Shaders/triangle.vert +++ b/src/Engine.Graphics.Vulkan/Shaders/triangle.vert @@ -10,18 +10,11 @@ layout(row_major, set = 0, binding = 0) uniform CameraUBO { mat4 vp; }; -layout(push_constant) uniform PC { - float angle; +layout(row_major, push_constant) uniform PC { + mat4 model; } pc; void main() { - float c = cos(pc.angle); - float s = sin(pc.angle); - vec3 rotated = vec3( - inPosition.x * c - inPosition.y * s, - inPosition.x * s + inPosition.y * c, - inPosition.z - ); - gl_Position = vp * vec4(rotated, 1.0); + gl_Position = vp * pc.model * vec4(inPosition, 1.0); fragColor = inColor; } diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.vert.spv b/src/Engine.Graphics.Vulkan/Shaders/triangle.vert.spv index c1afffda8ef72fcf926693c0216e4bab1e307ccf..edfeddb33d4d1e99fac4fcba0ca89073fe8ad874 100644 GIT binary patch literal 1544 zcmZ9L+iuf95QaB#oE8c_PiMn5Gybu5I<`;P7BDD^3QF9zP^v z)p$0Lm^E|O+ATdNVVnZaO9Dw>a!)dlc=|P^e=Es1E&b;4zV2BOK246sQT{2)i|Cu6 z*pkP_m(AiL&Qh^UHlF(}AE(7({5>Mh`M68IVG>W@#IqtykE7a0_xQdfN$vC1AANIt z{WheB;Uvn#kFVd$ZmVNBð1p4lv+wRdEXhs5~?n={EyqC}e5OVZQhvUVUlPU~^x z_wBAe=i%uvOS0VNwCwKUv}A~3k8%1g%O@da%}90G8?(NZADiQ}J;WM(SzbDK; zf_S)&EOzQ@;6nn%os)Jue^z0>K{)PNwQ*%#?g6h!IJ?5^ox1eZQ5Nq6j@lQbnFWs8 zVBQ5B{)?4OJh|-E7P-szvQp- zi;3^Edj^_rGo5+gnVp@TGv{R5Cp(;Lb6u{}y>(&rxppy5T;A)g-TmF^dgE|~qqkqSNU) zZwtDpa+H))f0y)$f{DE(yYBg{8aH4SCn$W0`sfnA&q;FX zqh@-;qQ)iZVsu=tMejH0e_ow|V|_UZcU%t8$J5hKwdkl=O{z87^pYOOmB5gbe#e!) zYV9~mWV)@ir_{PFKmBD*1|@&i_iFSMXZ*b_o}RJjJBu7}VMc;Bjy=S2cjK_(c;?i? z{X*`uyW%6>?yjM97hAaIdg;lyghl<}36BD%PiOZDJpxlR@$?8x9sT-8kE=Fn#U7NE zmo9k8>S;SKaHI0k!{E>QHvo(*DK~Ef_XQ6KcS{!}oIRD|3$j@S;T~zrHzYhLOm1qy zKP1ih@Zc-bh1YXxJo_DW=8DS2Vm4o`3cld50aG!+piO091lQ{?8^7PAY&2w2CyyY2?O&-p| zM;_{fClB+$yTA-FCsxl`${Q{SbN(;wp}uhmF$Ia`nUqbQ35o5yAsgQCP1&4pd2Y!j z2L5N7`?hTIW*F%k@Myd`Vqn{$?fIkNx;~9?S(p, "vkGetFenceStatus"); vkCreateBuffer = LoadDev(p, "vkCreateBuffer"); vkDestroyBuffer = LoadDev(p, "vkDestroyBuffer"); + vkCreateImage = LoadDev(p, "vkCreateImage"); + vkDestroyImage = LoadDev(p, "vkDestroyImage"); + vkGetImageMemoryRequirements = LoadDev(p, "vkGetImageMemoryRequirements"); + vkBindImageMemory = LoadDev(p, "vkBindImageMemory"); vkAllocateMemory = LoadDev(p, "vkAllocateMemory"); vkFreeMemory = LoadDev(p, "vkFreeMemory"); vkBindBufferMemory = LoadDev(p, "vkBindBufferMemory"); diff --git a/src/Engine.Graphics.Vulkan/VulkanEnums.cs b/src/Engine.Graphics.Vulkan/VulkanEnums.cs index 90fabd5..39a1292 100644 --- a/src/Engine.Graphics.Vulkan/VulkanEnums.cs +++ b/src/Engine.Graphics.Vulkan/VulkanEnums.cs @@ -61,6 +61,7 @@ public enum VkStructureType : int CommandBufferBeginInfo = 42, RenderPassBeginInfo = 43, ImageViewCreateInfo = 15, + ImageCreateInfo = 14, SemaphoreCreateInfo = 9, FenceCreateInfo = 8, SwapchainCreateInfoKHR = 1000001000, @@ -207,6 +208,31 @@ public enum VkFrontFace : int Clockwise = 1, } +public enum VkCompareOp : int +{ + Never = 0, + Less = 1, + Equal = 2, + LessOrEqual = 3, + Greater = 4, + NotEqual = 5, + GreaterOrEqual = 6, + Always = 7, +} + +public enum VkImageType : int +{ + Type1D = 0, + Type2D = 1, + Type3D = 2, +} + +public enum VkImageTiling : int +{ + Optimal = 0, + Linear = 1, +} + public enum VkBlendFactor : int { Zero = 0, @@ -283,6 +309,8 @@ public enum VkPipelineStageFlags2 : ulong public enum VkAccessFlags2 : ulong { None = 0, + DepthStencilAttachmentWrite = 0x00000400, + DepthStencilAttachmentRead = 0x00000200, ColorAttachmentRead = 0x00000080, ColorAttachmentWrite = 0x00000100, TransferRead = 0x00000800, diff --git a/src/Engine.Graphics.Vulkan/VulkanPipeline.cs b/src/Engine.Graphics.Vulkan/VulkanPipeline.cs index 6cfadfd..c4b7544 100644 --- a/src/Engine.Graphics.Vulkan/VulkanPipeline.cs +++ b/src/Engine.Graphics.Vulkan/VulkanPipeline.cs @@ -14,7 +14,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable private readonly VkDevice _device; private bool _disposed; - public VulkanPipeline(VkDevice device, VkFormat colorFormat, byte[] vertSpv, byte[] fragSpv) + public VulkanPipeline(VkDevice device, VkFormat colorFormat, VkFormat depthFormat, byte[] vertSpv, byte[] fragSpv) { _device = device; VertModule = CreateShaderModule(vertSpv); @@ -115,6 +115,16 @@ internal sealed unsafe class VulkanPipeline : IDisposable sampleShadingEnable = VkBool32.False, }; + var depthStencilState = new VkPipelineDepthStencilStateCreateInfo + { + sType = VkStructureType.PipelineDepthStencilStateCreateInfo, + depthTestEnable = VkBool32.True, + depthWriteEnable = VkBool32.True, + depthCompareOp = VkCompareOp.Less, + depthBoundsTestEnable = VkBool32.False, + stencilTestEnable = VkBool32.False, + }; + var blendAttachment = new VkPipelineColorBlendAttachmentState { blendEnable = VkBool32.False, @@ -144,7 +154,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable { stageFlags = VkShaderStageFlags.Vertex, offset = 0, - size = 4, + size = 64, }; var descLayout = DescriptorSetLayout; @@ -169,6 +179,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable sType = VkStructureType.PipelineRenderingCreateInfo, colorAttachmentCount = 1, pColorAttachmentFormats = &colorFormat, + depthAttachmentFormat = depthFormat, }; var pipelineInfo = new VkGraphicsPipelineCreateInfo @@ -182,6 +193,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable pViewportState = &viewportState, pRasterizationState = &rasterizationState, pMultisampleState = &multisampleState, + pDepthStencilState = &depthStencilState, pColorBlendState = &colorBlendState, pDynamicState = &dynamicState, layout = PipelineLayout, diff --git a/src/Engine.Graphics.Vulkan/VulkanRenderContext.cs b/src/Engine.Graphics.Vulkan/VulkanRenderContext.cs index 6a5b3e5..e08d583 100644 --- a/src/Engine.Graphics.Vulkan/VulkanRenderContext.cs +++ b/src/Engine.Graphics.Vulkan/VulkanRenderContext.cs @@ -23,7 +23,7 @@ internal sealed class VulkanRenderContext : IRenderContext }; _swapchain = new VulkanSwapchain(_ctx.Device, _ctx.PhysicalDevice, _ctx.Surface, - surfaceFormat, window.Width, window.Height); + surfaceFormat, window.Width, window.Height, _ctx); } public IRenderer CreateRenderer() diff --git a/src/Engine.Graphics.Vulkan/VulkanRenderer.cs b/src/Engine.Graphics.Vulkan/VulkanRenderer.cs index c145f98..07b6e5f 100644 --- a/src/Engine.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Engine.Graphics.Vulkan/VulkanRenderer.cs @@ -34,7 +34,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen var vertSpv = LoadShader("Shaders/triangle.vert.spv"); var fragSpv = LoadShader("Shaders/triangle.frag.spv"); - _pipeline = new VulkanPipeline(ctx.Device, swapchain.Format, vertSpv, fragSpv); + _pipeline = new VulkanPipeline(ctx.Device, swapchain.Format, swapchain.DepthFormat, vertSpv, fragSpv); _frameResources = new VulkanFrameResources(ctx.Device, ctx.GraphicsQueueFamilyIndex, swapchain.ImageCount, ctx, _pipeline.DescriptorSetLayout); @@ -109,11 +109,21 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen 0, 0, 0x400, 0x100); + TransitionImageLayoutDepth(cmd, _swapchain.DepthImage, + VkImageLayout.Undefined, VkImageLayout.DepthStencilAttachmentOptimal, + 0, 0, + 0x100, 0x200); + var clearValue = new VkClearValue { Color = new VkClearColorValue { Float0 = 0.02f, Float1 = 0.02f, Float2 = 0.02f, Float3 = 1.0f }, }; + var depthClearValue = new VkClearValue + { + DepthStencil = new VkClearDepthStencilValue { Depth = 1.0f, Stencil = 0 }, + }; + var colorAttachment = new VkRenderingAttachmentInfo { sType = VkStructureType.RenderingAttachmentInfo, @@ -124,6 +134,16 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen clearValue = clearValue, }; + var depthAttachment = new VkRenderingAttachmentInfo + { + sType = VkStructureType.RenderingAttachmentInfo, + imageView = _swapchain.DepthImageView, + imageLayout = VkImageLayout.DepthStencilAttachmentOptimal, + loadOp = VkAttachmentLoadOp.Clear, + storeOp = VkAttachmentStoreOp.Store, + clearValue = depthClearValue, + }; + var renderingInfo = new VkRenderingInfo { sType = VkStructureType.RenderingInfo, @@ -135,6 +155,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen layerCount = 1, colorAttachmentCount = 1, pColorAttachments = &colorAttachment, + pDepthAttachment = &depthAttachment, }; Vk.vkCmdBeginRendering(cmd, &renderingInfo); @@ -168,7 +189,8 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen Vk.vkCmdBindIndexBuffer(cmd, _indexBuffer.Buffer, 0, 0); var angle = _totalTime; - Vk.vkCmdPushConstants(cmd, _pipeline.PipelineLayout, VkShaderStageFlags.Vertex, 0, 4, &angle); + var model = Matrix4x4.CreateRotationY(angle) * Matrix4x4.CreateRotationX(angle * 0.5f); + Vk.vkCmdPushConstants(cmd, _pipeline.PipelineLayout, VkShaderStageFlags.Vertex, 0, 64, &model); Vk.vkCmdDrawIndexed(cmd, _indexCount, 1, 0, 0, 0); @@ -282,6 +304,39 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen Vk.vkCmdPipelineBarrier2(cmd, &depInfo); } + private static void TransitionImageLayoutDepth(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.Depth, + 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)) diff --git a/src/Engine.Graphics.Vulkan/VulkanStructs.cs b/src/Engine.Graphics.Vulkan/VulkanStructs.cs index bc6f822..08e2d21 100644 --- a/src/Engine.Graphics.Vulkan/VulkanStructs.cs +++ b/src/Engine.Graphics.Vulkan/VulkanStructs.cs @@ -671,7 +671,7 @@ public unsafe struct VkGraphicsPipelineCreateInfo public VkPipelineViewportStateCreateInfo* pViewportState; public VkPipelineRasterizationStateCreateInfo* pRasterizationState; public VkPipelineMultisampleStateCreateInfo* pMultisampleState; - public nint pDepthStencilState; + public VkPipelineDepthStencilStateCreateInfo* pDepthStencilState; public VkPipelineColorBlendStateCreateInfo* pColorBlendState; public VkPipelineDynamicStateCreateInfo* pDynamicState; public VkPipelineLayout layout; @@ -792,7 +792,7 @@ public unsafe struct VkRenderingInfo public uint viewMask; public uint colorAttachmentCount; public VkRenderingAttachmentInfo* pColorAttachments; - public nint pDepthAttachment; + public VkRenderingAttachmentInfo* pDepthAttachment; public nint pStencilAttachment; } @@ -908,6 +908,55 @@ public unsafe struct VkDebugUtilsMessengerCallbackDataEXT public nint pObjects; } +[StructLayout(LayoutKind.Sequential)] +public unsafe struct VkStencilOpState +{ + public int failOp; + public int passOp; + public int depthFailOp; + public int compareOp; + public uint compareMask; + public uint writeMask; + public uint reference; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct VkPipelineDepthStencilStateCreateInfo +{ + public VkStructureType sType; + public nint pNext; + public uint flags; + public VkBool32 depthTestEnable; + public VkBool32 depthWriteEnable; + public VkCompareOp depthCompareOp; + public VkBool32 depthBoundsTestEnable; + public VkBool32 stencilTestEnable; + public VkStencilOpState front; + public VkStencilOpState back; + public float minDepthBounds; + public float maxDepthBounds; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct VkImageCreateInfo +{ + public VkStructureType sType; + public nint pNext; + public uint flags; + public VkImageType imageType; + public VkFormat format; + public VkExtent3D extent; + public uint mipLevels; + public uint arrayLayers; + public VkSampleCountFlags samples; + public VkImageTiling tiling; + public VkImageUsageFlags usage; + public VkSharingMode sharingMode; + public uint queueFamilyIndexCount; + public uint* pQueueFamilyIndices; + public VkImageLayout initialLayout; +} + [StructLayout(LayoutKind.Sequential)] public unsafe struct VkPushConstantRange { diff --git a/src/Engine.Graphics.Vulkan/VulkanSwapchain.cs b/src/Engine.Graphics.Vulkan/VulkanSwapchain.cs index 95c99b8..18103ad 100644 --- a/src/Engine.Graphics.Vulkan/VulkanSwapchain.cs +++ b/src/Engine.Graphics.Vulkan/VulkanSwapchain.cs @@ -11,19 +11,26 @@ internal sealed unsafe class VulkanSwapchain : IDisposable public VkExtent2D Extent; public uint ImageCount; + public VkImage DepthImage; + public VkDeviceMemory DepthImageMemory; + public VkImageView DepthImageView; + public VkFormat DepthFormat = VkFormat.D32Sfloat; + private readonly VkDevice _device; private readonly VkPhysicalDevice _physicalDevice; private readonly VkSurfaceKHR _surface; private readonly VkSurfaceFormatKHR _surfaceFormat; + private readonly VulkanContext _ctx; private bool _disposed; public VulkanSwapchain(VkDevice device, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, - VkSurfaceFormatKHR surfaceFormat, int width, int height) + VkSurfaceFormatKHR surfaceFormat, int width, int height, VulkanContext ctx) { _device = device; _physicalDevice = physicalDevice; _surface = surface; _surfaceFormat = surfaceFormat; + _ctx = ctx; Create(width, height); } @@ -132,6 +139,81 @@ internal sealed unsafe class VulkanSwapchain : IDisposable } Console.WriteLine($"[Vulkan] Swapchain: {actualCount} images, {Extent.Width}x{Extent.Height}, format={Format}"); + + CreateDepthResources(); + } + + private void CreateDepthResources() + { + var imageInfo = new VkImageCreateInfo + { + sType = VkStructureType.ImageCreateInfo, + imageType = VkImageType.Type2D, + format = DepthFormat, + extent = new VkExtent3D { Width = Extent.Width, Height = Extent.Height, Depth = 1 }, + mipLevels = 1, + arrayLayers = 1, + samples = VkSampleCountFlags.Count1, + tiling = VkImageTiling.Optimal, + usage = VkImageUsageFlags.DepthStencilAttachment, + sharingMode = VkSharingMode.Exclusive, + initialLayout = VkImageLayout.Undefined, + }; + + var depthImg = VkImage.Null; + var result = Vk.vkCreateImage(_device, &imageInfo, 0, &depthImg); + if (result != VkResult.Success) + throw new InvalidOperationException($"vkCreateImage (depth) failed: {result}"); + DepthImage = depthImg; + + var reqs = new VkMemoryRequirements(); + Vk.vkGetImageMemoryRequirements(_device, DepthImage, &reqs); + + var memTypeIndex = _ctx.FindMemoryType(reqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal); + + var allocInfo = new VkMemoryAllocateInfo + { + sType = VkStructureType.MemoryAllocateInfo, + allocationSize = reqs.size, + memoryTypeIndex = memTypeIndex, + }; + + var depthMem = VkDeviceMemory.Null; + result = Vk.vkAllocateMemory(_device, &allocInfo, 0, &depthMem); + if (result != VkResult.Success) + throw new InvalidOperationException($"vkAllocateMemory (depth) failed: {result}"); + DepthImageMemory = depthMem; + + Vk.vkBindImageMemory(_device, DepthImage, DepthImageMemory, 0); + + var viewInfo = new VkImageViewCreateInfo + { + sType = VkStructureType.ImageViewCreateInfo, + image = DepthImage, + viewType = VkImageViewType.Type2D, + format = DepthFormat, + components = new VkComponentMapping + { + R = VkComponentSwizzle.Identity, + G = VkComponentSwizzle.Identity, + B = VkComponentSwizzle.Identity, + A = VkComponentSwizzle.Identity, + }, + subresourceRange = new VkImageSubresourceRange + { + AspectMask = VkImageAspectFlags.Depth, + BaseMipLevel = 0, + LevelCount = 1, + BaseArrayLayer = 0, + LayerCount = 1, + }, + }; + + var depthView = VkImageView.Null; + result = Vk.vkCreateImageView(_device, &viewInfo, 0, &depthView); + if (result != VkResult.Success) + throw new InvalidOperationException($"vkCreateImageView (depth) failed: {result}"); + DepthImageView = depthView; } public void Recreate(int width, int height) @@ -143,6 +225,22 @@ internal sealed unsafe class VulkanSwapchain : IDisposable private void Cleanup() { + if (DepthImageView.Handle != 0) + { + Vk.vkDestroyImageView(_device, DepthImageView, 0); + DepthImageView = VkImageView.Null; + } + if (DepthImage.Handle != 0) + { + Vk.vkDestroyImage(_device, DepthImage, 0); + DepthImage = VkImage.Null; + } + if (DepthImageMemory.Handle != 0) + { + Vk.vkFreeMemory(_device, DepthImageMemory, 0); + DepthImageMemory = VkDeviceMemory.Null; + } + for (int i = 0; i < ImageViews.Length; i++) { if (ImageViews[i].Handle != 0)