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 c1afffd..edfeddb 100644 Binary files a/src/Engine.Graphics.Vulkan/Shaders/triangle.vert.spv and b/src/Engine.Graphics.Vulkan/Shaders/triangle.vert.spv differ diff --git a/src/Engine.Graphics.Vulkan/Vk.cs b/src/Engine.Graphics.Vulkan/Vk.cs index 233a20d..5b7f519 100644 --- a/src/Engine.Graphics.Vulkan/Vk.cs +++ b/src/Engine.Graphics.Vulkan/Vk.cs @@ -47,6 +47,10 @@ internal static unsafe class Vk public delegate VkResult VkGetFenceStatus(VkDevice device, VkFence fence); public delegate VkResult VkCreateBuffer(VkDevice device, VkBufferCreateInfo* pCreateInfo, nint pAllocator, VkBuffer* pBuffer); public delegate void VkDestroyBuffer(VkDevice device, VkBuffer buffer, nint pAllocator); + public delegate VkResult VkCreateImage(VkDevice device, VkImageCreateInfo* pCreateInfo, nint pAllocator, VkImage* pImage); + public delegate void VkDestroyImage(VkDevice device, VkImage image, nint pAllocator); + public delegate void VkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements); + public delegate VkResult VkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, ulong memoryOffset); public delegate VkResult VkAllocateMemory(VkDevice device, VkMemoryAllocateInfo* pAllocateInfo, nint pAllocator, VkDeviceMemory* pMemory); public delegate void VkFreeMemory(VkDevice device, VkDeviceMemory memory, nint pAllocator); public delegate VkResult VkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, ulong memoryOffset); @@ -124,6 +128,10 @@ internal static unsafe class Vk public static VkGetFenceStatus vkGetFenceStatus; public static VkCreateBuffer vkCreateBuffer; public static VkDestroyBuffer vkDestroyBuffer; + public static VkCreateImage vkCreateImage; + public static VkDestroyImage vkDestroyImage; + public static VkGetImageMemoryRequirements vkGetImageMemoryRequirements; + public static VkBindImageMemory vkBindImageMemory; public static VkAllocateMemory vkAllocateMemory; public static VkFreeMemory vkFreeMemory; public static VkBindBufferMemory vkBindBufferMemory; @@ -208,6 +216,10 @@ internal static unsafe class Vk vkGetFenceStatus = LoadDev(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)