From b32469d42daab70c06d51d272d3660b2b6e181c8 Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Tue, 16 Jun 2026 18:29:35 +0300 Subject: [PATCH] fix: migrate to Silk.NET.Vulkan so the window actually runs on Kubuntu --- src/CortexEngine.App/Program.cs | 13 +- src/Engine.Graphics/ClearRenderer.cs | 183 -------------- src/Engine.Graphics/Engine.Graphics.csproj | 3 +- src/Engine.Graphics/Shaders/fragment.spv | Bin 500 -> 500 bytes src/Engine.Graphics/Shaders/vertex.spv | Bin 1136 -> 1008 bytes src/Engine.Graphics/Swapchain.cs | 281 +++++++++++---------- src/Engine.Graphics/TriangleRenderer.cs | 191 +++++++------- src/Engine.Graphics/VertexBuffer.cs | 80 +++--- src/Engine.Graphics/VulkanContext.cs | 269 ++++++++++---------- src/Engine.Graphics/VulkanPipeline.cs | 229 +++++++++-------- 10 files changed, 539 insertions(+), 710 deletions(-) delete mode 100644 src/Engine.Graphics/ClearRenderer.cs diff --git a/src/CortexEngine.App/Program.cs b/src/CortexEngine.App/Program.cs index 66a07ca..3efa5f1 100644 --- a/src/CortexEngine.App/Program.cs +++ b/src/CortexEngine.App/Program.cs @@ -12,15 +12,17 @@ class Program try { - using var window = new Sdl3Window("Cortex Engine — Step 1", 1280, 720); + using var window = new Sdl3Window("Cortex Engine — Step 2", 1280, 720); var timing = new Timing(); var input = new InputMapping(); - using var vulkan = new VulkanContext(window, enableValidation: true); + using var vulkan = new VulkanContext(window, enableValidation: false); using var swapchain = new Swapchain(vulkan); using var renderer = new TriangleRenderer(vulkan, swapchain); var frames = 0; var lastFpsTime = 0.0; + var lastWidth = window.Width; + var lastHeight = window.Height; while (!window.ShouldClose) { @@ -30,6 +32,13 @@ class Program // Note: SDL events are already polled in PumpEvents. // In a real engine, the window would expose an event iterator. + if (window.Width != lastWidth || window.Height != lastHeight) + { + lastWidth = window.Width; + lastHeight = window.Height; + swapchain.Recreate(lastWidth, lastHeight); + } + renderer.RenderFrame(); frames++; diff --git a/src/Engine.Graphics/ClearRenderer.cs b/src/Engine.Graphics/ClearRenderer.cs deleted file mode 100644 index 959da14..0000000 --- a/src/Engine.Graphics/ClearRenderer.cs +++ /dev/null @@ -1,183 +0,0 @@ -using System; -using Vortice.Vulkan; - -namespace Engine.Graphics; - -/// -/// Minimal renderer that clears the swapchain image to a solid color. -/// Serves as the foundational Step 1 rendering proof-of-concept. -/// -public sealed unsafe class ClearRenderer : IDisposable -{ - private readonly VulkanContext _context; - private readonly Swapchain _swapchain; - private VkCommandPool _commandPool; - private VkCommandBuffer[] _commandBuffers = null!; - private VkSemaphore[] _imageAvailableSemaphores = null!; - private VkSemaphore[] _renderFinishedSemaphores = null!; - private VkFence[] _inFlightFences = null!; - private int _currentFrame; - - public ClearRenderer(VulkanContext context, Swapchain swapchain) - { - _context = context; - _swapchain = swapchain; - CreateCommandPool(); - CreateCommandBuffers(); - CreateSyncObjects(); - } - - private void CreateCommandPool() - { - var createInfo = new VkCommandPoolCreateInfo - { - sType = VkStructureType.CommandPoolCreateInfo, - queueFamilyIndex = _context.GraphicsFamilyIndex, - flags = VkCommandPoolCreateFlags.ResetCommandBuffer - }; - - var result = _context.DeviceApi.vkCreateCommandPool(&createInfo, null, out _commandPool); - if (result != VkResult.Success) - throw new InvalidOperationException($"vkCreateCommandPool failed: {result}"); - } - - private void CreateCommandBuffers() - { - _commandBuffers = new VkCommandBuffer[2]; - for (var i = 0; i < _commandBuffers.Length; i++) - { - var allocInfo = new VkCommandBufferAllocateInfo - { - sType = VkStructureType.CommandBufferAllocateInfo, - commandPool = _commandPool, - level = VkCommandBufferLevel.Primary, - commandBufferCount = 1 - }; - - var result = _context.DeviceApi.vkAllocateCommandBuffer(&allocInfo, out _commandBuffers[i]); - if (result != VkResult.Success) - throw new InvalidOperationException($"vkAllocateCommandBuffers failed: {result}"); - } - } - - private void CreateSyncObjects() - { - _imageAvailableSemaphores = new VkSemaphore[2]; - _renderFinishedSemaphores = new VkSemaphore[2]; - _inFlightFences = new VkFence[2]; - - var semaphoreInfo = new VkSemaphoreCreateInfo { sType = VkStructureType.SemaphoreCreateInfo }; - var fenceInfo = new VkFenceCreateInfo - { - sType = VkStructureType.FenceCreateInfo, - flags = VkFenceCreateFlags.Signaled - }; - - for (var i = 0; i < 2; i++) - { - _context.DeviceApi.vkCreateSemaphore(&semaphoreInfo, null, out _imageAvailableSemaphores[i]); - _context.DeviceApi.vkCreateSemaphore(&semaphoreInfo, null, out _renderFinishedSemaphores[i]); - _context.DeviceApi.vkCreateFence(&fenceInfo, null, out _inFlightFences[i]); - } - } - - public void RenderFrame(float r, float g, float b) - { - var frame = _currentFrame % 2; - - _context.DeviceApi.vkWaitForFences(_inFlightFences[frame], true, ulong.MaxValue); - _context.DeviceApi.vkResetFences(_inFlightFences[frame]); - - var result = _context.DeviceApi.vkAcquireNextImageKHR( - _swapchain.Handle, - ulong.MaxValue, - _imageAvailableSemaphores[frame], - VkFence.Null, - out var imageIndex); - - if (result == VkResult.ErrorOutOfDateKHR) - { - return; - } - - var cmd = _commandBuffers[frame]; - _context.DeviceApi.vkResetCommandBuffer(cmd, VkCommandBufferResetFlags.None); - - var beginInfo = new VkCommandBufferBeginInfo - { - sType = VkStructureType.CommandBufferBeginInfo, - flags = VkCommandBufferUsageFlags.OneTimeSubmit - }; - _context.DeviceApi.vkBeginCommandBuffer(cmd, &beginInfo); - - var clearColor = new VkClearValue(r, g, b, 1.0f); - - var renderPassInfo = new VkRenderPassBeginInfo - { - sType = VkStructureType.RenderPassBeginInfo, - renderPass = _swapchain.RenderPass, - framebuffer = _swapchain.Framebuffers[imageIndex], - renderArea = new VkRect2D(0, 0, _swapchain.Extent.width, _swapchain.Extent.height), - clearValueCount = 1, - pClearValues = &clearColor - }; - - _context.DeviceApi.vkCmdBeginRenderPass(cmd, &renderPassInfo, VkSubpassContents.Inline); - _context.DeviceApi.vkCmdEndRenderPass(cmd); - _context.DeviceApi.vkEndCommandBuffer(cmd); - - var waitSemaphore = _imageAvailableSemaphores[frame]; - var signalSemaphore = _renderFinishedSemaphores[frame]; - var stageMask = VkPipelineStageFlags.ColorAttachmentOutput; - var submitInfo = new VkSubmitInfo - { - sType = VkStructureType.SubmitInfo, - waitSemaphoreCount = 1, - pWaitSemaphores = &waitSemaphore, - pWaitDstStageMask = &stageMask, - commandBufferCount = 1, - pCommandBuffers = &cmd, - signalSemaphoreCount = 1, - pSignalSemaphores = &signalSemaphore - }; - - _context.DeviceApi.vkQueueSubmit(_context.GraphicsQueue, 1, &submitInfo, _inFlightFences[frame]); - - var swapchain = _swapchain.Handle; - var presentInfo = new VkPresentInfoKHR - { - sType = VkStructureType.PresentInfoKHR, - waitSemaphoreCount = 1, - pWaitSemaphores = &signalSemaphore, - swapchainCount = 1, - pSwapchains = &swapchain, - pImageIndices = &imageIndex - }; - - var presentResult = _context.DeviceApi.vkQueuePresentKHR(_context.PresentQueue, &presentInfo); - if (presentResult == VkResult.ErrorOutOfDateKHR || presentResult == VkResult.SuboptimalKHR) - { - // Recreate handled externally. - } - - _currentFrame++; - } - - public void Dispose() - { - _context.DeviceApi.vkDeviceWaitIdle(); - - for (var i = 0; i < 2; i++) - { - if (_renderFinishedSemaphores[i] != VkSemaphore.Null) - _context.DeviceApi.vkDestroySemaphore(_renderFinishedSemaphores[i]); - if (_imageAvailableSemaphores[i] != VkSemaphore.Null) - _context.DeviceApi.vkDestroySemaphore(_imageAvailableSemaphores[i]); - if (_inFlightFences[i] != VkFence.Null) - _context.DeviceApi.vkDestroyFence(_inFlightFences[i]); - } - - if (_commandPool != VkCommandPool.Null) - _context.DeviceApi.vkDestroyCommandPool(_commandPool); - } -} diff --git a/src/Engine.Graphics/Engine.Graphics.csproj b/src/Engine.Graphics/Engine.Graphics.csproj index b5eed86..3f59fa0 100644 --- a/src/Engine.Graphics/Engine.Graphics.csproj +++ b/src/Engine.Graphics/Engine.Graphics.csproj @@ -18,7 +18,8 @@ - + + diff --git a/src/Engine.Graphics/Shaders/fragment.spv b/src/Engine.Graphics/Shaders/fragment.spv index a0856c950447405d1fa4518a0406ac280ac98fd7..828f1e56b1fa987e9bc5cdb272d43039ab4f4c08 100644 GIT binary patch delta 23 ecmeyu{Dqm5nMs+Qfq{{Mdm?8vBm2hGentR0v;@`w delta 23 ecmeyu{Dqm5nMs+Qfq{{MaUy3lBge+nentR0RRqld diff --git a/src/Engine.Graphics/Shaders/vertex.spv b/src/Engine.Graphics/Shaders/vertex.spv index f05ea4555e8dc755e7653995942db5aeed50197b..791f44ad0f2e9337698a7d7d7391eb1397110276 100644 GIT binary patch delta 66 zcmeys@qwL}nMs+Qfq{{Mn}LHtaU!oNBj-eC#fdpjINceTfr1PSOp_}aLnq&2)SIlq VbcRuI@>izWlMgU&V6gyd0szW`4|Mx*_&xsF9Eh& /// Manages the Vulkan swapchain, image views, render pass, and framebuffers. -/// Recreates itself automatically when the window is resized. +/// Uses Silk.NET.Vulkan. /// public sealed unsafe class Swapchain : IDisposable { private readonly VulkanContext _context; - private VkRenderPass _renderPass; - private VkSwapchainKHR _swapchain; - private VkImage[] _images = null!; - private VkImageView[] _imageViews = null!; - private VkFramebuffer[] _framebuffers = null!; - private VkSurfaceFormatKHR _surfaceFormat; - private VkPresentModeKHR _presentMode; - private VkExtent2D _extent; + private RenderPass _renderPass; + private SwapchainKHR _swapchain; + private Image[] _images = null!; + private ImageView[] _imageViews = null!; + private Framebuffer[] _framebuffers = null!; + private SurfaceFormatKHR _surfaceFormat; + private PresentModeKHR _presentMode; + private Extent2D _extent; - public VkRenderPass RenderPass => _renderPass; - public VkFramebuffer[] Framebuffers => _framebuffers; - public VkExtent2D Extent => _extent; - public VkSwapchainKHR Handle => _swapchain; + public RenderPass RenderPass => _renderPass; + public Framebuffer[] Framebuffers => _framebuffers; + public Extent2D Extent => _extent; + public SwapchainKHR Handle => _swapchain; public uint ImageCount => (uint)_images.Length; public Swapchain(VulkanContext context) @@ -35,7 +37,7 @@ public sealed unsafe class Swapchain : IDisposable public void Recreate(int width, int height) { - _context.DeviceApi.vkDeviceWaitIdle(); + _context.Vk.DeviceWaitIdle(_context.Device); CleanupSwapchain(); var capabilities = GetSurfaceCapabilities(); @@ -43,60 +45,63 @@ public sealed unsafe class Swapchain : IDisposable _presentMode = ChoosePresentMode(); _extent = ChooseExtent(capabilities, (uint)width, (uint)height); - var imageCount = capabilities.minImageCount + 1; - if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) - imageCount = capabilities.maxImageCount; + var imageCount = capabilities.MinImageCount + 1; + if (capabilities.MaxImageCount > 0 && imageCount > capabilities.MaxImageCount) + imageCount = capabilities.MaxImageCount; - var createInfo = new VkSwapchainCreateInfoKHR + var createInfo = new SwapchainCreateInfoKHR { - sType = VkStructureType.SwapchainCreateInfoKHR, - surface = _context.Surface, - minImageCount = imageCount, - imageFormat = _surfaceFormat.format, - imageColorSpace = _surfaceFormat.colorSpace, - imageExtent = _extent, - imageArrayLayers = 1, - imageUsage = VkImageUsageFlags.ColorAttachment, - imageSharingMode = VkSharingMode.Exclusive, - preTransform = capabilities.currentTransform, - compositeAlpha = VkCompositeAlphaFlagsKHR.Opaque, - presentMode = _presentMode, - clipped = true, - oldSwapchain = _swapchain + SType = StructureType.SwapchainCreateInfoKhr, + Surface = _context.Surface, + MinImageCount = imageCount, + ImageFormat = _surfaceFormat.Format, + ImageColorSpace = _surfaceFormat.ColorSpace, + ImageExtent = _extent, + ImageArrayLayers = 1, + ImageUsage = ImageUsageFlags.ColorAttachmentBit, + ImageSharingMode = SharingMode.Exclusive, + PreTransform = capabilities.CurrentTransform, + CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, + PresentMode = _presentMode, + Clipped = true, + OldSwapchain = _swapchain }; - var result = _context.DeviceApi.vkCreateSwapchainKHR(&createInfo, null, out _swapchain); - if (result != VkResult.Success) + SwapchainKHR swapchain; + var result = _context.KhrSwapchain!.CreateSwapchain(_context.Device, &createInfo, null, &swapchain); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateSwapchainKHR failed: {result}"); + _swapchain = swapchain; _images = GetSwapchainImages(); - _imageViews = new VkImageView[_images.Length]; - _framebuffers = new VkFramebuffer[_images.Length]; + _imageViews = new ImageView[_images.Length]; + _framebuffers = new Framebuffer[_images.Length]; for (var i = 0; i < _images.Length; i++) { - _imageViews[i] = CreateImageView(_images[i], _surfaceFormat.format); + _imageViews[i] = CreateImageView(_images[i], _surfaceFormat.Format); _framebuffers[i] = CreateFramebuffer(_imageViews[i]); } } - private VkSurfaceCapabilitiesKHR GetSurfaceCapabilities() + private SurfaceCapabilitiesKHR GetSurfaceCapabilities() { - var result = _context.InstanceApi.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(_context.PhysicalDevice, _context.Surface, out var capabilities); - if (result != VkResult.Success) + SurfaceCapabilitiesKHR capabilities; + var result = _context.KhrSurface!.GetPhysicalDeviceSurfaceCapabilities(_context.PhysicalDevice, _context.Surface, &capabilities); + if (result != Result.Success) throw new InvalidOperationException($"vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: {result}"); return capabilities; } - private VkImage[] GetSwapchainImages() + private Image[] GetSwapchainImages() { uint count = 0; - _context.DeviceApi.vkGetSwapchainImagesKHR(_swapchain, &count, null); - var images = new VkImage[count]; - fixed (VkImage* p = images) + _context.KhrSwapchain!.GetSwapchainImages(_context.Device, _swapchain, &count, null); + var images = new Image[count]; + fixed (Image* p = images) { - var result = _context.DeviceApi.vkGetSwapchainImagesKHR(_swapchain, &count, p); - if (result != VkResult.Success) + var result = _context.KhrSwapchain!.GetSwapchainImages(_context.Device, _swapchain, &count, p); + if (result != Result.Success) throw new InvalidOperationException($"vkGetSwapchainImagesKHR failed: {result}"); } return images; @@ -104,167 +109,169 @@ public sealed unsafe class Swapchain : IDisposable private void CreateRenderPass() { - var colorAttachment = new VkAttachmentDescription + var colorAttachment = new AttachmentDescription { - format = _surfaceFormat.format != VkFormat.Undefined ? _surfaceFormat.format : VkFormat.B8G8R8A8Unorm, - samples = VkSampleCountFlags.Count1, - loadOp = VkAttachmentLoadOp.Clear, - storeOp = VkAttachmentStoreOp.Store, - stencilLoadOp = VkAttachmentLoadOp.DontCare, - stencilStoreOp = VkAttachmentStoreOp.DontCare, - initialLayout = VkImageLayout.Undefined, - finalLayout = VkImageLayout.PresentSrcKHR + Format = _surfaceFormat.Format != Format.Undefined ? _surfaceFormat.Format : Format.B8G8R8A8Unorm, + Samples = SampleCountFlags.Count1Bit, + LoadOp = AttachmentLoadOp.Clear, + StoreOp = AttachmentStoreOp.Store, + StencilLoadOp = AttachmentLoadOp.DontCare, + StencilStoreOp = AttachmentStoreOp.DontCare, + InitialLayout = ImageLayout.Undefined, + FinalLayout = ImageLayout.PresentSrcKhr }; - var colorAttachmentRef = new VkAttachmentReference + var colorAttachmentRef = new AttachmentReference { - attachment = 0, - layout = VkImageLayout.ColorAttachmentOptimal + Attachment = 0, + Layout = ImageLayout.ColorAttachmentOptimal }; - var subpass = new VkSubpassDescription + var subpass = new SubpassDescription { - pipelineBindPoint = VkPipelineBindPoint.Graphics, - colorAttachmentCount = 1, - pColorAttachments = &colorAttachmentRef + PipelineBindPoint = PipelineBindPoint.Graphics, + ColorAttachmentCount = 1, + PColorAttachments = &colorAttachmentRef }; - var dependency = new VkSubpassDependency + var dependency = new SubpassDependency { - srcSubpass = Vulkan.VK_SUBPASS_EXTERNAL, - dstSubpass = 0, - srcStageMask = VkPipelineStageFlags.ColorAttachmentOutput, - dstStageMask = VkPipelineStageFlags.ColorAttachmentOutput, - srcAccessMask = VkAccessFlags.None, - dstAccessMask = VkAccessFlags.ColorAttachmentWrite + SrcSubpass = ~0u, + DstSubpass = 0, + SrcStageMask = PipelineStageFlags.ColorAttachmentOutputBit, + DstStageMask = PipelineStageFlags.ColorAttachmentOutputBit, + SrcAccessMask = AccessFlags.None, + DstAccessMask = AccessFlags.ColorAttachmentWriteBit }; - var createInfo = new VkRenderPassCreateInfo + var createInfo = new RenderPassCreateInfo { - sType = VkStructureType.RenderPassCreateInfo, - attachmentCount = 1, - pAttachments = &colorAttachment, - subpassCount = 1, - pSubpasses = &subpass, - dependencyCount = 1, - pDependencies = &dependency + SType = StructureType.RenderPassCreateInfo, + AttachmentCount = 1, + PAttachments = &colorAttachment, + SubpassCount = 1, + PSubpasses = &subpass, + DependencyCount = 1, + PDependencies = &dependency }; - var result = _context.DeviceApi.vkCreateRenderPass(&createInfo, null, out _renderPass); - if (result != VkResult.Success) + RenderPass renderPass; + var result = _context.Vk.CreateRenderPass(_context.Device, &createInfo, null, &renderPass); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateRenderPass failed: {result}"); + _renderPass = renderPass; } - private VkImageView CreateImageView(VkImage image, VkFormat format) + private ImageView CreateImageView(Image image, Format format) { - var createInfo = new VkImageViewCreateInfo + var createInfo = new ImageViewCreateInfo { - sType = VkStructureType.ImageViewCreateInfo, - image = image, - viewType = VkImageViewType.Image2D, - format = format, - components = new VkComponentMapping(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A), - subresourceRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, 1, 0, 1) + SType = StructureType.ImageViewCreateInfo, + Image = image, + ViewType = ImageViewType.Type2D, + Format = format, + Components = new ComponentMapping(ComponentSwizzle.R, ComponentSwizzle.G, ComponentSwizzle.B, ComponentSwizzle.A), + SubresourceRange = new ImageSubresourceRange(ImageAspectFlags.ColorBit, 0, 1, 0, 1) }; - var result = _context.DeviceApi.vkCreateImageView(&createInfo, null, out var imageView); - if (result != VkResult.Success) + ImageView imageView; + var result = _context.Vk.CreateImageView(_context.Device, &createInfo, null, &imageView); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateImageView failed: {result}"); - return imageView; } - private VkFramebuffer CreateFramebuffer(VkImageView imageView) + private Framebuffer CreateFramebuffer(ImageView imageView) { - var createInfo = new VkFramebufferCreateInfo + var createInfo = new FramebufferCreateInfo { - sType = VkStructureType.FramebufferCreateInfo, - renderPass = _renderPass, - attachmentCount = 1, - pAttachments = &imageView, - width = _extent.width, - height = _extent.height, - layers = 1 + SType = StructureType.FramebufferCreateInfo, + RenderPass = _renderPass, + AttachmentCount = 1, + PAttachments = &imageView, + Width = _extent.Width, + Height = _extent.Height, + Layers = 1 }; - var result = _context.DeviceApi.vkCreateFramebuffer(&createInfo, null, out var framebuffer); - if (result != VkResult.Success) + Framebuffer framebuffer; + var result = _context.Vk.CreateFramebuffer(_context.Device, &createInfo, null, &framebuffer); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateFramebuffer failed: {result}"); - return framebuffer; } - private VkSurfaceFormatKHR ChooseSurfaceFormat() + private SurfaceFormatKHR ChooseSurfaceFormat() { var formats = GetSurfaceFormats(); foreach (var format in formats) { - if (format.format == VkFormat.B8G8R8A8Unorm && format.colorSpace == VkColorSpaceKHR.SrgbNonLinear) + if (format.Format == Format.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.SpaceSrgbNonlinearKhr) return format; } return formats[0]; } - private VkSurfaceFormatKHR[] GetSurfaceFormats() + private SurfaceFormatKHR[] GetSurfaceFormats() { uint count = 0; - _context.InstanceApi.vkGetPhysicalDeviceSurfaceFormatsKHR(_context.PhysicalDevice, _context.Surface, &count, null); - var formats = new VkSurfaceFormatKHR[count]; - fixed (VkSurfaceFormatKHR* p = formats) + _context.KhrSurface!.GetPhysicalDeviceSurfaceFormats(_context.PhysicalDevice, _context.Surface, &count, null); + var formats = new SurfaceFormatKHR[count]; + fixed (SurfaceFormatKHR* p = formats) { - var result = _context.InstanceApi.vkGetPhysicalDeviceSurfaceFormatsKHR(_context.PhysicalDevice, _context.Surface, &count, p); - if (result != VkResult.Success) + var result = _context.KhrSurface!.GetPhysicalDeviceSurfaceFormats(_context.PhysicalDevice, _context.Surface, &count, p); + if (result != Result.Success) throw new InvalidOperationException($"vkGetPhysicalDeviceSurfaceFormatsKHR failed: {result}"); } return formats; } - private VkPresentModeKHR ChoosePresentMode() + private PresentModeKHR ChoosePresentMode() { var modes = GetSurfacePresentModes(); - if (Array.Exists(modes, m => m == VkPresentModeKHR.Mailbox)) - return VkPresentModeKHR.Mailbox; - return VkPresentModeKHR.Fifo; + if (Array.Exists(modes, m => m == PresentModeKHR.MailboxKhr)) + return PresentModeKHR.MailboxKhr; + return PresentModeKHR.FifoKhr; } - private VkPresentModeKHR[] GetSurfacePresentModes() + private PresentModeKHR[] GetSurfacePresentModes() { uint count = 0; - _context.InstanceApi.vkGetPhysicalDeviceSurfacePresentModesKHR(_context.PhysicalDevice, _context.Surface, &count, null); - var modes = new VkPresentModeKHR[count]; - fixed (VkPresentModeKHR* p = modes) + _context.KhrSurface!.GetPhysicalDeviceSurfacePresentModes(_context.PhysicalDevice, _context.Surface, &count, null); + var modes = new PresentModeKHR[count]; + fixed (PresentModeKHR* p = modes) { - var result = _context.InstanceApi.vkGetPhysicalDeviceSurfacePresentModesKHR(_context.PhysicalDevice, _context.Surface, &count, p); - if (result != VkResult.Success) + var result = _context.KhrSurface!.GetPhysicalDeviceSurfacePresentModes(_context.PhysicalDevice, _context.Surface, &count, p); + if (result != Result.Success) throw new InvalidOperationException($"vkGetPhysicalDeviceSurfacePresentModesKHR failed: {result}"); } return modes; } - private VkExtent2D ChooseExtent(VkSurfaceCapabilitiesKHR capabilities, uint width, uint height) + private Extent2D ChooseExtent(SurfaceCapabilitiesKHR capabilities, uint width, uint height) { - if (capabilities.currentExtent.width != uint.MaxValue) - return capabilities.currentExtent; + if (capabilities.CurrentExtent.Width != uint.MaxValue) + return capabilities.CurrentExtent; - var extent = new VkExtent2D + var extent = new Extent2D { - width = Math.Clamp(width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width), - height = Math.Clamp(height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height) + Width = Math.Clamp(width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width), + Height = Math.Clamp(height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height) }; return extent; } private void CleanupSwapchain() { - if (_context.Device == VkDevice.Null) + if (_context.Device.Handle == 0) return; if (_framebuffers != null) { foreach (var fb in _framebuffers) { - if (fb != VkFramebuffer.Null) - _context.DeviceApi.vkDestroyFramebuffer(fb); + if (fb.Handle != 0) + _context.Vk.DestroyFramebuffer(_context.Device, fb, null); } } @@ -272,21 +279,21 @@ public sealed unsafe class Swapchain : IDisposable { foreach (var view in _imageViews) { - if (view != VkImageView.Null) - _context.DeviceApi.vkDestroyImageView(view); + if (view.Handle != 0) + _context.Vk.DestroyImageView(_context.Device, view, null); } } - if (_swapchain != VkSwapchainKHR.Null) - _context.DeviceApi.vkDestroySwapchainKHR(_swapchain); + if (_swapchain.Handle != 0) + _context.KhrSwapchain!.DestroySwapchain(_context.Device, _swapchain, null); } public void Dispose() { - _context.DeviceApi.vkDeviceWaitIdle(); + _context.Vk.DeviceWaitIdle(_context.Device); CleanupSwapchain(); - if (_renderPass != VkRenderPass.Null) - _context.DeviceApi.vkDestroyRenderPass(_renderPass); + if (_renderPass.Handle != 0) + _context.Vk.DestroyRenderPass(_context.Device, _renderPass, null); } } diff --git a/src/Engine.Graphics/TriangleRenderer.cs b/src/Engine.Graphics/TriangleRenderer.cs index 1b0b5f8..1437dae 100644 --- a/src/Engine.Graphics/TriangleRenderer.cs +++ b/src/Engine.Graphics/TriangleRenderer.cs @@ -1,10 +1,12 @@ using System; -using Vortice.Vulkan; +using Silk.NET.Core; +using Silk.NET.Vulkan; namespace Engine.Graphics; /// /// Renders a colored triangle using a vertex buffer and a simple graphics pipeline. +/// Uses Silk.NET.Vulkan. /// public sealed unsafe class TriangleRenderer : IDisposable { @@ -12,11 +14,11 @@ public sealed unsafe class TriangleRenderer : IDisposable private readonly Swapchain _swapchain; private readonly VulkanPipeline _pipeline; private readonly VertexBuffer _vertexBuffer; - private VkCommandPool _commandPool; - private VkCommandBuffer[] _commandBuffers = null!; - private VkSemaphore[] _imageAvailableSemaphores = null!; - private VkSemaphore[] _renderFinishedSemaphores = null!; - private VkFence[] _inFlightFences = null!; + private CommandPool _commandPool; + private CommandBuffer[] _commandBuffers = null!; + private Silk.NET.Vulkan.Semaphore[] _imageAvailableSemaphores = null!; + private Silk.NET.Vulkan.Semaphore[] _renderFinishedSemaphores = null!; + private Silk.NET.Vulkan.Fence[] _inFlightFences = null!; private int _currentFrame; public TriangleRenderer(VulkanContext context, Swapchain swapchain) @@ -26,7 +28,6 @@ public sealed unsafe class TriangleRenderer : IDisposable _pipeline = new VulkanPipeline(context, swapchain); _vertexBuffer = CreateTriangleBuffer(); - CreateCommandPool(); CreateCommandBuffers(); CreateSyncObjects(); @@ -36,7 +37,6 @@ public sealed unsafe class TriangleRenderer : IDisposable { var vertices = new[] { - // Position (vec2) + Color (vec3) 0.0f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f @@ -46,7 +46,7 @@ public sealed unsafe class TriangleRenderer : IDisposable fixed (byte* p = bytes) fixed (float* v = vertices) { - Buffer.MemoryCopy(v, p, bytes.Length, vertices.Length * sizeof(float)); + global::System.Buffer.MemoryCopy(v, p, bytes.Length, vertices.Length * sizeof(float)); } return new VertexBuffer(_context, bytes); @@ -54,55 +54,64 @@ public sealed unsafe class TriangleRenderer : IDisposable private void CreateCommandPool() { - var createInfo = new VkCommandPoolCreateInfo + var createInfo = new CommandPoolCreateInfo { - sType = VkStructureType.CommandPoolCreateInfo, - queueFamilyIndex = _context.GraphicsFamilyIndex, - flags = VkCommandPoolCreateFlags.ResetCommandBuffer + SType = StructureType.CommandPoolCreateInfo, + QueueFamilyIndex = _context.GraphicsFamilyIndex, + Flags = CommandPoolCreateFlags.ResetCommandBufferBit }; - var result = _context.DeviceApi.vkCreateCommandPool(&createInfo, null, out _commandPool); - if (result != VkResult.Success) + CommandPool commandPool; + var result = _context.Vk.CreateCommandPool(_context.Device, &createInfo, null, &commandPool); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateCommandPool failed: {result}"); + _commandPool = commandPool; } private void CreateCommandBuffers() { - _commandBuffers = new VkCommandBuffer[2]; + _commandBuffers = new CommandBuffer[2]; for (var i = 0; i < _commandBuffers.Length; i++) { - var allocInfo = new VkCommandBufferAllocateInfo + var allocInfo = new CommandBufferAllocateInfo { - sType = VkStructureType.CommandBufferAllocateInfo, - commandPool = _commandPool, - level = VkCommandBufferLevel.Primary, - commandBufferCount = 1 + SType = StructureType.CommandBufferAllocateInfo, + CommandPool = _commandPool, + Level = CommandBufferLevel.Primary, + CommandBufferCount = 1 }; - var result = _context.DeviceApi.vkAllocateCommandBuffer(&allocInfo, out _commandBuffers[i]); - if (result != VkResult.Success) + CommandBuffer cmd; + var result = _context.Vk.AllocateCommandBuffers(_context.Device, &allocInfo, &cmd); + if (result != Result.Success) throw new InvalidOperationException($"vkAllocateCommandBuffers failed: {result}"); + _commandBuffers[i] = cmd; } } private void CreateSyncObjects() { - _imageAvailableSemaphores = new VkSemaphore[2]; - _renderFinishedSemaphores = new VkSemaphore[2]; - _inFlightFences = new VkFence[2]; + _imageAvailableSemaphores = new Silk.NET.Vulkan.Semaphore[2]; + _renderFinishedSemaphores = new Silk.NET.Vulkan.Semaphore[2]; + _inFlightFences = new Silk.NET.Vulkan.Fence[2]; - var semaphoreInfo = new VkSemaphoreCreateInfo { sType = VkStructureType.SemaphoreCreateInfo }; - var fenceInfo = new VkFenceCreateInfo + var semaphoreInfo = new SemaphoreCreateInfo { SType = StructureType.SemaphoreCreateInfo }; + var fenceInfo = new FenceCreateInfo { - sType = VkStructureType.FenceCreateInfo, - flags = VkFenceCreateFlags.Signaled + SType = StructureType.FenceCreateInfo, + Flags = FenceCreateFlags.SignaledBit }; for (var i = 0; i < 2; i++) { - _context.DeviceApi.vkCreateSemaphore(&semaphoreInfo, null, out _imageAvailableSemaphores[i]); - _context.DeviceApi.vkCreateSemaphore(&semaphoreInfo, null, out _renderFinishedSemaphores[i]); - _context.DeviceApi.vkCreateFence(&fenceInfo, null, out _inFlightFences[i]); + Silk.NET.Vulkan.Semaphore imageAvailable, renderFinished; + Silk.NET.Vulkan.Fence fence; + _context.Vk.CreateSemaphore(_context.Device, &semaphoreInfo, null, &imageAvailable); + _context.Vk.CreateSemaphore(_context.Device, &semaphoreInfo, null, &renderFinished); + _context.Vk.CreateFence(_context.Device, &fenceInfo, null, &fence); + _imageAvailableSemaphores[i] = imageAvailable; + _renderFinishedSemaphores[i] = renderFinished; + _inFlightFences[i] = fence; } } @@ -110,106 +119,96 @@ public sealed unsafe class TriangleRenderer : IDisposable { var frame = _currentFrame % 2; - _context.DeviceApi.vkWaitForFences(_inFlightFences[frame], true, ulong.MaxValue); - _context.DeviceApi.vkResetFences(_inFlightFences[frame]); + var fence = _inFlightFences[frame]; + _context.Vk.WaitForFences(_context.Device, 1, &fence, true, ulong.MaxValue); + _context.Vk.ResetFences(_context.Device, 1, &fence); - var result = _context.DeviceApi.vkAcquireNextImageKHR( - _swapchain.Handle, - ulong.MaxValue, - _imageAvailableSemaphores[frame], - VkFence.Null, - out var imageIndex); - - if (result == VkResult.ErrorOutOfDateKHR) + uint imageIndex; + var result = _context.KhrSwapchain!.AcquireNextImage(_context.Device, _swapchain.Handle, ulong.MaxValue, _imageAvailableSemaphores[frame], new Silk.NET.Vulkan.Fence(), &imageIndex); + if (result == Result.ErrorOutOfDateKhr) return; var cmd = _commandBuffers[frame]; - _context.DeviceApi.vkResetCommandBuffer(cmd, VkCommandBufferResetFlags.None); + _context.Vk.ResetCommandBuffer(cmd, CommandBufferResetFlags.None); - var beginInfo = new VkCommandBufferBeginInfo + var beginInfo = new CommandBufferBeginInfo { - sType = VkStructureType.CommandBufferBeginInfo, - flags = VkCommandBufferUsageFlags.OneTimeSubmit + SType = StructureType.CommandBufferBeginInfo, + Flags = CommandBufferUsageFlags.OneTimeSubmitBit }; - _context.DeviceApi.vkBeginCommandBuffer(cmd, &beginInfo); + _context.Vk.BeginCommandBuffer(cmd, &beginInfo); - var clearColor = new VkClearValue(0.0f, 0.0f, 0.0f, 1.0f); - - var renderPassInfo = new VkRenderPassBeginInfo + var clearColor = new ClearValue(new ClearColorValue(0.0f, 0.0f, 0.0f, 1.0f)); + var renderPassInfo = new RenderPassBeginInfo { - sType = VkStructureType.RenderPassBeginInfo, - renderPass = _swapchain.RenderPass, - framebuffer = _swapchain.Framebuffers[imageIndex], - renderArea = new VkRect2D(0, 0, _swapchain.Extent.width, _swapchain.Extent.height), - clearValueCount = 1, - pClearValues = &clearColor + SType = StructureType.RenderPassBeginInfo, + RenderPass = _swapchain.RenderPass, + Framebuffer = _swapchain.Framebuffers[imageIndex], + RenderArea = new Rect2D(new Offset2D(0, 0), _swapchain.Extent), + ClearValueCount = 1, + PClearValues = &clearColor }; - _context.DeviceApi.vkCmdBeginRenderPass(cmd, &renderPassInfo, VkSubpassContents.Inline); + _context.Vk.CmdBeginRenderPass(cmd, &renderPassInfo, SubpassContents.Inline); + _context.Vk.CmdBindPipeline(cmd, PipelineBindPoint.Graphics, _pipeline.Handle); - _context.DeviceApi.vkCmdBindPipeline(cmd, VkPipelineBindPoint.Graphics, _pipeline.Handle); - - var viewport = new VkViewport(0, 0, _swapchain.Extent.width, _swapchain.Extent.height, 0, 1); - var scissor = new VkRect2D(0, 0, _swapchain.Extent.width, _swapchain.Extent.height); - _context.DeviceApi.vkCmdSetViewport(cmd, 0, viewport); - _context.DeviceApi.vkCmdSetScissor(cmd, 0, scissor); + var viewport = new Viewport(0, 0, _swapchain.Extent.Width, _swapchain.Extent.Height, 0, 1); + var scissor = new Rect2D(new Offset2D(0, 0), _swapchain.Extent); + _context.Vk.CmdSetViewport(cmd, 0, 1, &viewport); + _context.Vk.CmdSetScissor(cmd, 0, 1, &scissor); var vertexBuffer = _vertexBuffer.Buffer; var offset = 0ul; - _context.DeviceApi.vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBuffer, &offset); - _context.DeviceApi.vkCmdDraw(cmd, 3, 1, 0, 0); + _context.Vk.CmdBindVertexBuffers(cmd, 0, 1, &vertexBuffer, &offset); + _context.Vk.CmdDraw(cmd, 3, 1, 0, 0); - _context.DeviceApi.vkCmdEndRenderPass(cmd); - _context.DeviceApi.vkEndCommandBuffer(cmd); + _context.Vk.CmdEndRenderPass(cmd); + _context.Vk.EndCommandBuffer(cmd); var waitSemaphore = _imageAvailableSemaphores[frame]; var signalSemaphore = _renderFinishedSemaphores[frame]; - var stageMask = VkPipelineStageFlags.ColorAttachmentOutput; - var submitInfo = new VkSubmitInfo + var stageMask = PipelineStageFlags.ColorAttachmentOutputBit; + var submitInfo = new SubmitInfo { - sType = VkStructureType.SubmitInfo, - waitSemaphoreCount = 1, - pWaitSemaphores = &waitSemaphore, - pWaitDstStageMask = &stageMask, - commandBufferCount = 1, - pCommandBuffers = &cmd, - signalSemaphoreCount = 1, - pSignalSemaphores = &signalSemaphore + SType = StructureType.SubmitInfo, + WaitSemaphoreCount = 1, + PWaitSemaphores = &waitSemaphore, + PWaitDstStageMask = &stageMask, + CommandBufferCount = 1, + PCommandBuffers = &cmd, + SignalSemaphoreCount = 1, + PSignalSemaphores = &signalSemaphore }; - _context.DeviceApi.vkQueueSubmit(_context.GraphicsQueue, 1, &submitInfo, _inFlightFences[frame]); + _context.Vk.QueueSubmit(_context.GraphicsQueue, 1, &submitInfo, _inFlightFences[frame]); var swapchain = _swapchain.Handle; - var presentInfo = new VkPresentInfoKHR + var presentInfo = new PresentInfoKHR { - sType = VkStructureType.PresentInfoKHR, - waitSemaphoreCount = 1, - pWaitSemaphores = &signalSemaphore, - swapchainCount = 1, - pSwapchains = &swapchain, - pImageIndices = &imageIndex + SType = StructureType.PresentInfoKhr, + WaitSemaphoreCount = 1, + PWaitSemaphores = &signalSemaphore, + SwapchainCount = 1, + PSwapchains = &swapchain, + PImageIndices = &imageIndex }; - _context.DeviceApi.vkQueuePresentKHR(_context.PresentQueue, &presentInfo); + _context.KhrSwapchain!.QueuePresent(_context.PresentQueue, &presentInfo); _currentFrame++; } public void Dispose() { - _context.DeviceApi.vkDeviceWaitIdle(); + _context.Vk.DeviceWaitIdle(_context.Device); for (var i = 0; i < 2; i++) { - if (_renderFinishedSemaphores[i] != VkSemaphore.Null) - _context.DeviceApi.vkDestroySemaphore(_renderFinishedSemaphores[i]); - if (_imageAvailableSemaphores[i] != VkSemaphore.Null) - _context.DeviceApi.vkDestroySemaphore(_imageAvailableSemaphores[i]); - if (_inFlightFences[i] != VkFence.Null) - _context.DeviceApi.vkDestroyFence(_inFlightFences[i]); + _context.Vk.DestroySemaphore(_context.Device, _renderFinishedSemaphores[i], null); + _context.Vk.DestroySemaphore(_context.Device, _imageAvailableSemaphores[i], null); + _context.Vk.DestroyFence(_context.Device, _inFlightFences[i], null); } - if (_commandPool != VkCommandPool.Null) - _context.DeviceApi.vkDestroyCommandPool(_commandPool); + _context.Vk.DestroyCommandPool(_context.Device, _commandPool, null); _vertexBuffer.Dispose(); _pipeline.Dispose(); diff --git a/src/Engine.Graphics/VertexBuffer.cs b/src/Engine.Graphics/VertexBuffer.cs index ba3f2a1..a840cef 100644 --- a/src/Engine.Graphics/VertexBuffer.cs +++ b/src/Engine.Graphics/VertexBuffer.cs @@ -1,16 +1,18 @@ using System; -using Vortice.Vulkan; +using Silk.NET.Core; +using Silk.NET.Vulkan; namespace Engine.Graphics; /// -/// Interleaved vertex: vec2 position + vec3 color. +/// Interleaved vertex buffer: vec2 position + vec3 color. +/// Uses Silk.NET.Vulkan. /// public sealed unsafe class VertexBuffer : IDisposable { private readonly VulkanContext _context; - public VkBuffer Buffer { get; } - public VkDeviceMemory Memory { get; } + public Silk.NET.Vulkan.Buffer Buffer { get; } + public DeviceMemory Memory { get; } public ulong Size { get; } public VertexBuffer(VulkanContext context, ReadOnlySpan data) @@ -18,62 +20,66 @@ public sealed unsafe class VertexBuffer : IDisposable _context = context; Size = (ulong)data.Length; - Buffer = CreateBuffer(Size, VkBufferUsageFlags.VertexBuffer); + Buffer = CreateBuffer(Size, BufferUsageFlags.VertexBufferBit); var memoryRequirements = GetMemoryRequirements(Buffer); - Memory = AllocateMemory(memoryRequirements, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); + Memory = AllocateMemory(memoryRequirements, MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit); - var result = _context.DeviceApi.vkBindBufferMemory(Buffer, Memory, 0); - if (result != VkResult.Success) + var result = _context.Vk.BindBufferMemory(_context.Device, Buffer, Memory, 0); + if (result != Result.Success) throw new InvalidOperationException($"vkBindBufferMemory failed: {result}"); CopyData(data); } - private VkBuffer CreateBuffer(ulong size, VkBufferUsageFlags usage) + private Silk.NET.Vulkan.Buffer CreateBuffer(ulong size, BufferUsageFlags usage) { - var createInfo = new VkBufferCreateInfo + var createInfo = new BufferCreateInfo { - sType = VkStructureType.BufferCreateInfo, - size = size, - usage = usage, - sharingMode = VkSharingMode.Exclusive + SType = StructureType.BufferCreateInfo, + Size = size, + Usage = usage, + SharingMode = SharingMode.Exclusive }; - var result = _context.DeviceApi.vkCreateBuffer(&createInfo, null, out var buffer); - if (result != VkResult.Success) + Silk.NET.Vulkan.Buffer buffer; + var result = _context.Vk.CreateBuffer(_context.Device, &createInfo, null, &buffer); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateBuffer failed: {result}"); return buffer; } - private VkMemoryRequirements GetMemoryRequirements(VkBuffer buffer) + private MemoryRequirements GetMemoryRequirements(Silk.NET.Vulkan.Buffer buffer) { - _context.DeviceApi.vkGetBufferMemoryRequirements(buffer, out var requirements); + MemoryRequirements requirements; + _context.Vk.GetBufferMemoryRequirements(_context.Device, buffer, &requirements); return requirements; } - private VkDeviceMemory AllocateMemory(VkMemoryRequirements requirements, VkMemoryPropertyFlags properties) + private DeviceMemory AllocateMemory(MemoryRequirements requirements, MemoryPropertyFlags properties) { - var memoryTypeIndex = FindMemoryType(requirements.memoryTypeBits, properties); - var allocateInfo = new VkMemoryAllocateInfo + var memoryTypeIndex = FindMemoryType(requirements.MemoryTypeBits, properties); + var allocateInfo = new MemoryAllocateInfo { - sType = VkStructureType.MemoryAllocateInfo, - allocationSize = requirements.size, - memoryTypeIndex = memoryTypeIndex + SType = StructureType.MemoryAllocateInfo, + AllocationSize = requirements.Size, + MemoryTypeIndex = memoryTypeIndex }; - var result = _context.DeviceApi.vkAllocateMemory(&allocateInfo, null, out var memory); - if (result != VkResult.Success) + DeviceMemory memory; + var result = _context.Vk.AllocateMemory(_context.Device, &allocateInfo, null, &memory); + if (result != Result.Success) throw new InvalidOperationException($"vkAllocateMemory failed: {result}"); return memory; } - private uint FindMemoryType(uint typeFilter, VkMemoryPropertyFlags properties) + private uint FindMemoryType(uint typeFilter, MemoryPropertyFlags properties) { - _context.InstanceApi.vkGetPhysicalDeviceMemoryProperties(_context.PhysicalDevice, out var memoryProperties); - for (var i = 0; i < memoryProperties.memoryTypeCount; i++) + PhysicalDeviceMemoryProperties memoryProperties; + _context.Vk.GetPhysicalDeviceMemoryProperties(_context.PhysicalDevice, &memoryProperties); + for (var i = 0; i < memoryProperties.MemoryTypeCount; i++) { if ((typeFilter & (1u << i)) != 0 && - (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + (memoryProperties.MemoryTypes[i].PropertyFlags & properties) == properties) { return (uint)i; } @@ -84,22 +90,22 @@ public sealed unsafe class VertexBuffer : IDisposable private void CopyData(ReadOnlySpan data) { void* mappedData; - var result = _context.DeviceApi.vkMapMemory(Memory, 0, Size, VkMemoryMapFlags.None, &mappedData); - if (result != VkResult.Success) + var result = _context.Vk.MapMemory(_context.Device, Memory, 0, Size, MemoryMapFlags.None, &mappedData); + if (result != Result.Success) throw new InvalidOperationException($"vkMapMemory failed: {result}"); fixed (byte* src = data) { - System.Buffer.MemoryCopy(src, mappedData, (long)Size, data.Length); + global::System.Buffer.MemoryCopy(src, mappedData, (long)Size, data.Length); } - _context.DeviceApi.vkUnmapMemory(Memory); + _context.Vk.UnmapMemory(_context.Device, Memory); } public void Dispose() { - _context.DeviceApi.vkDeviceWaitIdle(); - _context.DeviceApi.vkDestroyBuffer(Buffer); - _context.DeviceApi.vkFreeMemory(Memory); + _context.Vk.DeviceWaitIdle(_context.Device); + _context.Vk.DestroyBuffer(_context.Device, Buffer, null); + _context.Vk.FreeMemory(_context.Device, Memory, null); } } diff --git a/src/Engine.Graphics/VulkanContext.cs b/src/Engine.Graphics/VulkanContext.cs index 496f531..cced80f 100644 --- a/src/Engine.Graphics/VulkanContext.cs +++ b/src/Engine.Graphics/VulkanContext.cs @@ -5,37 +5,42 @@ using System.Runtime.InteropServices; using System.Text; using Engine.Core; using SDL; -using Vortice.Vulkan; +using Silk.NET.Core; +using Silk.NET.Core.Native; +using Silk.NET.Vulkan; +using Silk.NET.Vulkan.Extensions.KHR; namespace Engine.Graphics; /// -/// Owns the Vulkan instance, physical device, logical device, queues, and API handles. -/// Created once per application lifetime. +/// Owns the Vulkan instance, physical device, logical device, queues, and surface. +/// Uses Silk.NET.Vulkan because Vortice.Vulkan's loader segfaulted on this Kubuntu setup. /// public sealed unsafe class VulkanContext : IDisposable { private bool _disposed; - public VkInstance Instance { get; private set; } - public VkInstanceApi InstanceApi { get; private set; } - public VkPhysicalDevice PhysicalDevice { get; private set; } - public VkDevice Device { get; private set; } - public VkDeviceApi DeviceApi { get; private set; } - public VkQueue GraphicsQueue { get; private set; } - public VkQueue PresentQueue { get; private set; } + public Vk Vk { get; } + public KhrSurface? KhrSurface { get; private set; } + public KhrSwapchain? KhrSwapchain { get; private set; } + public Instance Instance { get; private set; } + public PhysicalDevice PhysicalDevice { get; private set; } + public Device Device { get; private set; } + public Queue GraphicsQueue { get; private set; } + public Queue PresentQueue { get; private set; } + public SurfaceKHR Surface { get; private set; } public uint GraphicsFamilyIndex { get; private set; } public uint PresentFamilyIndex { get; private set; } - public VkSurfaceKHR Surface { get; private set; } public VulkanContext(Sdl3Window window, bool enableValidation = true) { + Vk = Vk.GetApi(); CreateInstance(window, enableValidation); - InstanceApi = Vulkan.GetApi(Instance); + LoadInstanceExtensions(); CreateSurface(window); PickPhysicalDevice(); CreateLogicalDevice(); - DeviceApi = Vulkan.GetApi(Instance, Device); + LoadDeviceExtensions(); GetQueues(); } @@ -51,55 +56,74 @@ public sealed unsafe class VulkanContext : IDisposable ? new[] { "VK_LAYER_KHRONOS_validation" } : Array.Empty(); - var appName = VkStringInterop.ConvertToUnmanaged("Cortex Engine"); - var engineName = VkStringInterop.ConvertToUnmanaged("CortexEngine"); + var appName = SilkMarshal.StringToMemory("Cortex Engine", NativeStringEncoding.UTF8); + var engineName = SilkMarshal.StringToMemory("CortexEngine", NativeStringEncoding.UTF8); + var extensionMemory = SilkMarshal.StringArrayToMemory(requiredExtensions, NativeStringEncoding.UTF8); + var layerMemory = SilkMarshal.StringArrayToMemory(layerNames, NativeStringEncoding.UTF8); - var appInfo = new VkApplicationInfo + try { - sType = VkStructureType.ApplicationInfo, - pApplicationName = appName, - pEngineName = engineName, - apiVersion = VkVersion.Version_1_3 - }; - - using var extensionPin = new StringArrayPin(requiredExtensions); - using var layerPin = new StringArrayPin(layerNames); - - { - var createInfo = new VkInstanceCreateInfo + var appInfo = new ApplicationInfo { - sType = VkStructureType.InstanceCreateInfo, - pApplicationInfo = &appInfo, - enabledExtensionCount = (uint)requiredExtensions.Count, - ppEnabledExtensionNames = extensionPin.Pointers, - enabledLayerCount = (uint)layerNames.Length, - ppEnabledLayerNames = layerPin.Pointers + SType = StructureType.ApplicationInfo, + PApplicationName = (byte*)appName.Handle, + PEngineName = (byte*)engineName.Handle, + ApiVersion = Vk.Version13 }; - var result = Vulkan.vkCreateInstance(&createInfo, null, out var instance); - if (result != VkResult.Success) + var createInfo = new InstanceCreateInfo + { + SType = StructureType.InstanceCreateInfo, + PApplicationInfo = &appInfo, + EnabledExtensionCount = (uint)requiredExtensions.Count, + PpEnabledExtensionNames = (byte**)extensionMemory.Handle, + EnabledLayerCount = (uint)layerNames.Length, + PpEnabledLayerNames = (byte**)layerMemory.Handle + }; + + Instance instance; + var result = Vk.CreateInstance(&createInfo, null, &instance); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateInstance failed: {result}"); Instance = instance; } + finally + { + appName.Dispose(); + engineName.Dispose(); + extensionMemory.Dispose(); + layerMemory.Dispose(); + } + } - VkStringInterop.Free(appName); - VkStringInterop.Free(engineName); + private void LoadInstanceExtensions() + { + if (!Vk.TryGetInstanceExtension(Instance, out KhrSurface khrSurface)) + throw new InvalidOperationException("VK_KHR_surface not available."); + KhrSurface = khrSurface; + } + + private void LoadDeviceExtensions() + { + if (!Vk.TryGetDeviceExtension(Instance, Device, out KhrSwapchain khrSwapchain)) + throw new InvalidOperationException("VK_KHR_swapchain not available."); + KhrSwapchain = khrSwapchain; } private void CreateSurface(Sdl3Window window) { var sdlInstance = (SDL.VkInstance_T*)Instance.Handle; var sdlSurface = (SDL.VkSurfaceKHR_T*)null; - var result = SDL3.SDL_Vulkan_CreateSurface( + var sdlResult = SDL3.SDL_Vulkan_CreateSurface( (SDL_Window*)window.Handle, sdlInstance, null, &sdlSurface); - if (result != true) + if (sdlResult != true) throw new InvalidOperationException($"SDL_Vulkan_CreateSurface failed: {SDL3.SDL_GetError()}"); - Surface = new VkSurfaceKHR((ulong)sdlSurface); + Surface = new SurfaceKHR((ulong)sdlSurface); } private void PickPhysicalDevice() @@ -110,93 +134,108 @@ public sealed unsafe class VulkanContext : IDisposable foreach (var device in devices) { - var properties = InstanceApi.vkGetPhysicalDeviceProperties(device); + var properties = Vk.GetPhysicalDeviceProperties(device); var queueFamilies = GetPhysicalDeviceQueueFamilyProperties(device); var hasGraphics = false; var hasPresent = false; for (var i = 0; i < queueFamilies.Length; i++) { - if (queueFamilies[i].queueFlags.HasFlag(VkQueueFlags.Graphics)) + if (queueFamilies[i].QueueFlags.HasFlag(QueueFlags.GraphicsBit)) hasGraphics = true; - var supportResult = InstanceApi.vkGetPhysicalDeviceSurfaceSupportKHR(device, (uint)i, Surface, out VkBool32 supported); - if (supportResult == VkResult.Success && supported) + + Bool32 supported; + KhrSurface!.GetPhysicalDeviceSurfaceSupport(device, (uint)i, Surface, &supported); + if (supported) hasPresent = true; } if (hasGraphics && hasPresent) { PhysicalDevice = device; - if (properties.deviceType == VkPhysicalDeviceType.DiscreteGpu) + if (properties.DeviceType == PhysicalDeviceType.DiscreteGpu) break; } } - if (PhysicalDevice == VkPhysicalDevice.Null) + if (PhysicalDevice.Handle == 0) throw new InvalidOperationException("No suitable Vulkan physical device found."); } - private VkPhysicalDevice[] EnumeratePhysicalDevices() + private PhysicalDevice[] EnumeratePhysicalDevices() { uint count = 0; - InstanceApi.vkEnumeratePhysicalDevices(&count, null); + Vk.EnumeratePhysicalDevices(Instance, &count, null); if (count == 0) - return Array.Empty(); + return Array.Empty(); - var devices = new VkPhysicalDevice[count]; - fixed (VkPhysicalDevice* p = devices) + var devices = new PhysicalDevice[count]; + fixed (PhysicalDevice* p = devices) { - var result = InstanceApi.vkEnumeratePhysicalDevices(&count, p); - if (result != VkResult.Success) + var result = Vk.EnumeratePhysicalDevices(Instance, &count, p); + if (result != Result.Success) throw new InvalidOperationException($"vkEnumeratePhysicalDevices failed: {result}"); } return devices; } + private QueueFamilyProperties[] GetPhysicalDeviceQueueFamilyProperties(PhysicalDevice device) + { + uint count = 0; + Vk.GetPhysicalDeviceQueueFamilyProperties(device, &count, null); + var properties = new QueueFamilyProperties[count]; + fixed (QueueFamilyProperties* p = properties) + { + Vk.GetPhysicalDeviceQueueFamilyProperties(device, &count, p); + } + return properties; + } + private void CreateLogicalDevice() { var queueFamilies = GetPhysicalDeviceQueueFamilyProperties(PhysicalDevice); - GraphicsFamilyIndex = FindQueueFamilyIndex(queueFamilies, VkQueueFlags.Graphics); + GraphicsFamilyIndex = FindQueueFamilyIndex(queueFamilies, QueueFlags.GraphicsBit); PresentFamilyIndex = FindPresentQueueFamilyIndex(queueFamilies); var uniqueFamilies = new HashSet { GraphicsFamilyIndex, PresentFamilyIndex }; - var queueCreateInfos = uniqueFamilies.Select(family => new VkDeviceQueueCreateInfo + var queueCreateInfos = uniqueFamilies.Select(family => new DeviceQueueCreateInfo { - sType = VkStructureType.DeviceQueueCreateInfo, - queueFamilyIndex = family, - queueCount = 1 + SType = StructureType.DeviceQueueCreateInfo, + QueueFamilyIndex = family, + QueueCount = 1 }).ToArray(); - var priorityHandles = new GCHandle[queueCreateInfos.Length]; var extensionNames = new[] { "VK_KHR_swapchain" }; - using var extensionPin = new StringArrayPin(extensionNames); + var extensionMemory = SilkMarshal.StringArrayToMemory(extensionNames, NativeStringEncoding.UTF8); + var priorityHandles = new GCHandle[queueCreateInfos.Length]; try { - var deviceFeatures = new VkPhysicalDeviceFeatures(); + var deviceFeatures = new PhysicalDeviceFeatures(); for (var i = 0; i < queueCreateInfos.Length; i++) { var priority = new[] { 1.0f }; var handle = GCHandle.Alloc(priority, GCHandleType.Pinned); priorityHandles[i] = handle; - queueCreateInfos[i].pQueuePriorities = (float*)handle.AddrOfPinnedObject(); + queueCreateInfos[i].PQueuePriorities = (float*)handle.AddrOfPinnedObject(); } - fixed (VkDeviceQueueCreateInfo* pQueue = queueCreateInfos) + fixed (DeviceQueueCreateInfo* pQueue = queueCreateInfos) { - var createInfo = new VkDeviceCreateInfo + var createInfo = new DeviceCreateInfo { - sType = VkStructureType.DeviceCreateInfo, - queueCreateInfoCount = (uint)queueCreateInfos.Length, - pQueueCreateInfos = pQueue, - pEnabledFeatures = &deviceFeatures, - enabledExtensionCount = 1, - ppEnabledExtensionNames = extensionPin.Pointers + SType = StructureType.DeviceCreateInfo, + QueueCreateInfoCount = (uint)queueCreateInfos.Length, + PQueueCreateInfos = pQueue, + PEnabledFeatures = &deviceFeatures, + EnabledExtensionCount = 1, + PpEnabledExtensionNames = (byte**)extensionMemory.Handle }; - var result = InstanceApi.vkCreateDevice(PhysicalDevice, &createInfo, null, out var device); - if (result != VkResult.Success) + Device device; + var result = Vk.CreateDevice(PhysicalDevice, &createInfo, null, &device); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateDevice failed: {result}"); Device = device; } @@ -208,101 +247,55 @@ public sealed unsafe class VulkanContext : IDisposable if (handle.IsAllocated) handle.Free(); } + extensionMemory.Dispose(); } } private void GetQueues() { - DeviceApi.vkGetDeviceQueue(GraphicsFamilyIndex, 0, out var graphicsQueue); - DeviceApi.vkGetDeviceQueue(PresentFamilyIndex, 0, out var presentQueue); + Queue graphicsQueue; + Vk.GetDeviceQueue(Device, GraphicsFamilyIndex, 0, &graphicsQueue); GraphicsQueue = graphicsQueue; + + Queue presentQueue; + Vk.GetDeviceQueue(Device, PresentFamilyIndex, 0, &presentQueue); PresentQueue = presentQueue; } - private uint FindQueueFamilyIndex(VkQueueFamilyProperties[] properties, VkQueueFlags flags) + private uint FindQueueFamilyIndex(QueueFamilyProperties[] properties, QueueFlags flags) { for (var i = 0; i < properties.Length; i++) { - if (properties[i].queueFlags.HasFlag(flags)) + if (properties[i].QueueFlags.HasFlag(flags)) return (uint)i; } throw new InvalidOperationException($"No queue family with flags {flags} found."); } - private uint FindPresentQueueFamilyIndex(VkQueueFamilyProperties[] properties) + private uint FindPresentQueueFamilyIndex(QueueFamilyProperties[] properties) { for (var i = 0; i < properties.Length; i++) { - var supportResult = InstanceApi.vkGetPhysicalDeviceSurfaceSupportKHR(PhysicalDevice, (uint)i, Surface, out VkBool32 supported); - if (supportResult == VkResult.Success && supported) + Bool32 supported; + KhrSurface!.GetPhysicalDeviceSurfaceSupport(PhysicalDevice, (uint)i, Surface, &supported); + if (supported) return (uint)i; } throw new InvalidOperationException("No present queue family found."); } - private VkQueueFamilyProperties[] GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice device) - { - uint count = 0; - InstanceApi.vkGetPhysicalDeviceQueueFamilyProperties(device, &count, null); - var properties = new VkQueueFamilyProperties[count]; - fixed (VkQueueFamilyProperties* p = properties) - { - InstanceApi.vkGetPhysicalDeviceQueueFamilyProperties(device, &count, p); - } - return properties; - } - - private sealed unsafe class StringArrayPin : IDisposable - { - public byte** Pointers; - private readonly GCHandle[] _handles; - - public StringArrayPin(IReadOnlyList strings) - { - if (strings.Count == 0) - { - Pointers = null; - _handles = Array.Empty(); - return; - } - - Pointers = (byte**)Marshal.AllocHGlobal(strings.Count * sizeof(byte*)); - _handles = new GCHandle[strings.Count]; - - for (var i = 0; i < strings.Count; i++) - { - var bytes = Encoding.UTF8.GetBytes(strings[i] + '\0'); - _handles[i] = GCHandle.Alloc(bytes, GCHandleType.Pinned); - Pointers[i] = (byte*)_handles[i].AddrOfPinnedObject(); - } - } - - public void Dispose() - { - if (Pointers == null) - return; - - foreach (var handle in _handles) - { - if (handle.IsAllocated) - handle.Free(); - } - - Marshal.FreeHGlobal((nint)Pointers); - Pointers = null; - } - } - public void Dispose() { if (_disposed) return; _disposed = true; - if (Device != VkDevice.Null) - DeviceApi.vkDestroyDevice(); - if (Instance != VkInstance.Null && Surface != VkSurfaceKHR.Null) - InstanceApi.vkDestroySurfaceKHR(Surface); - if (Instance != VkInstance.Null) - InstanceApi.vkDestroyInstance(); + Vk.DeviceWaitIdle(Device); + if (Device.Handle != 0) + Vk.DestroyDevice(Device, null); + if (Surface.Handle != 0) + KhrSurface?.DestroySurface(Instance, Surface, null); + if (Instance.Handle != 0) + Vk.DestroyInstance(Instance, null); + Vk.Dispose(); } } diff --git a/src/Engine.Graphics/VulkanPipeline.cs b/src/Engine.Graphics/VulkanPipeline.cs index 5a74224..298cec0 100644 --- a/src/Engine.Graphics/VulkanPipeline.cs +++ b/src/Engine.Graphics/VulkanPipeline.cs @@ -1,21 +1,22 @@ using System; -using Vortice.Vulkan; +using Silk.NET.Core.Native; +using Silk.NET.Vulkan; namespace Engine.Graphics; /// -/// A simple graphics pipeline for a single vertex/fragment shader pair. -/// Assumes a triangle with vec2 position + vec3 color per vertex. +/// Simple graphics pipeline for a triangle with vec2 position + vec3 color. +/// Uses Silk.NET.Vulkan. /// public sealed unsafe class VulkanPipeline : IDisposable { private readonly VulkanContext _context; private readonly Swapchain _swapchain; - public VkPipeline Handle { get; } - public VkPipelineLayout Layout { get; } - private readonly VkShaderModule _vertexModule; - private readonly VkShaderModule _fragmentModule; + public Pipeline Handle { get; } + public PipelineLayout Layout { get; } + private readonly ShaderModule _vertexModule; + private readonly ShaderModule _fragmentModule; public VulkanPipeline(VulkanContext context, Swapchain swapchain) { @@ -29,7 +30,7 @@ public sealed unsafe class VulkanPipeline : IDisposable Handle = CreateGraphicsPipeline(); } - private VkShaderModule CreateShaderModule(string resourceName) + private ShaderModule CreateShaderModule(string resourceName) { var code = ShaderLoader.Load(resourceName); if (code.Length % 4 != 0) @@ -37,189 +38,185 @@ public sealed unsafe class VulkanPipeline : IDisposable fixed (byte* pCode = code) { - var createInfo = new VkShaderModuleCreateInfo + var createInfo = new ShaderModuleCreateInfo { - sType = VkStructureType.ShaderModuleCreateInfo, - codeSize = (nuint)code.Length, - pCode = (uint*)pCode + SType = StructureType.ShaderModuleCreateInfo, + CodeSize = (nuint)code.Length, + PCode = (uint*)pCode }; - var result = _context.DeviceApi.vkCreateShaderModule(&createInfo, null, out var module); - if (result != VkResult.Success) + ShaderModule module; + var result = _context.Vk.CreateShaderModule(_context.Device, &createInfo, null, &module); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateShaderModule failed for {resourceName}: {result}"); return module; } } - private VkPipelineLayout CreatePipelineLayout() + private PipelineLayout CreatePipelineLayout() { - var createInfo = new VkPipelineLayoutCreateInfo + var createInfo = new PipelineLayoutCreateInfo { - sType = VkStructureType.PipelineLayoutCreateInfo, - setLayoutCount = 0, - pushConstantRangeCount = 0 + SType = StructureType.PipelineLayoutCreateInfo, + SetLayoutCount = 0, + PushConstantRangeCount = 0 }; - var result = _context.DeviceApi.vkCreatePipelineLayout(&createInfo, null, out var layout); - if (result != VkResult.Success) + PipelineLayout layout; + var result = _context.Vk.CreatePipelineLayout(_context.Device, &createInfo, null, &layout); + if (result != Result.Success) throw new InvalidOperationException($"vkCreatePipelineLayout failed: {result}"); return layout; } - private VkPipeline CreateGraphicsPipeline() + private Pipeline CreateGraphicsPipeline() { + var entryName = SilkMarshal.StringToPtr("main", NativeStringEncoding.UTF8); var stages = new[] { - new VkPipelineShaderStageCreateInfo + new PipelineShaderStageCreateInfo { - sType = VkStructureType.PipelineShaderStageCreateInfo, - stage = VkShaderStageFlags.Vertex, - module = _vertexModule, - pName = VkStringInterop.ConvertToUnmanaged("main") + SType = StructureType.PipelineShaderStageCreateInfo, + Stage = ShaderStageFlags.VertexBit, + Module = _vertexModule, + PName = (byte*)entryName }, - new VkPipelineShaderStageCreateInfo + new PipelineShaderStageCreateInfo { - sType = VkStructureType.PipelineShaderStageCreateInfo, - stage = VkShaderStageFlags.Fragment, - module = _fragmentModule, - pName = VkStringInterop.ConvertToUnmanaged("main") + SType = StructureType.PipelineShaderStageCreateInfo, + Stage = ShaderStageFlags.FragmentBit, + Module = _fragmentModule, + PName = (byte*)entryName } }; - var bindingDescription = new VkVertexInputBindingDescription + var bindingDescription = new VertexInputBindingDescription { - binding = 0, - stride = (uint)(5 * sizeof(float)), - inputRate = VkVertexInputRate.Vertex + Binding = 0, + Stride = (uint)(5 * sizeof(float)), + InputRate = VertexInputRate.Vertex }; var attributeDescriptions = new[] { - new VkVertexInputAttributeDescription + new VertexInputAttributeDescription { - binding = 0, - location = 0, - format = VkFormat.R32G32Sfloat, - offset = 0 + Binding = 0, + Location = 0, + Format = Format.R32G32Sfloat, + Offset = 0 }, - new VkVertexInputAttributeDescription + new VertexInputAttributeDescription { - binding = 0, - location = 1, - format = VkFormat.R32G32B32Sfloat, - offset = (uint)(2 * sizeof(float)) + Binding = 0, + Location = 1, + Format = Format.R32G32B32Sfloat, + Offset = (uint)(2 * sizeof(float)) } }; - VkPipelineVertexInputStateCreateInfo vertexInputInfo; - fixed (VkVertexInputAttributeDescription* pAttributes = attributeDescriptions) + PipelineVertexInputStateCreateInfo vertexInputInfo; + fixed (VertexInputAttributeDescription* pAttributes = attributeDescriptions) { - vertexInputInfo = new VkPipelineVertexInputStateCreateInfo + vertexInputInfo = new PipelineVertexInputStateCreateInfo { - sType = VkStructureType.PipelineVertexInputStateCreateInfo, - vertexBindingDescriptionCount = 1, - pVertexBindingDescriptions = &bindingDescription, - vertexAttributeDescriptionCount = (uint)attributeDescriptions.Length, - pVertexAttributeDescriptions = pAttributes + SType = StructureType.PipelineVertexInputStateCreateInfo, + VertexBindingDescriptionCount = 1, + PVertexBindingDescriptions = &bindingDescription, + VertexAttributeDescriptionCount = (uint)attributeDescriptions.Length, + PVertexAttributeDescriptions = pAttributes }; } - var inputAssembly = new VkPipelineInputAssemblyStateCreateInfo + var inputAssembly = new PipelineInputAssemblyStateCreateInfo { - sType = VkStructureType.PipelineInputAssemblyStateCreateInfo, - topology = VkPrimitiveTopology.TriangleList, - primitiveRestartEnable = false + SType = StructureType.PipelineInputAssemblyStateCreateInfo, + Topology = PrimitiveTopology.TriangleList, + PrimitiveRestartEnable = false }; - var viewport = new VkViewport(0, 0, _swapchain.Extent.width, _swapchain.Extent.height, 0, 1); - var scissor = new VkRect2D(0, 0, _swapchain.Extent.width, _swapchain.Extent.height); - - var viewportState = new VkPipelineViewportStateCreateInfo + var viewportState = new PipelineViewportStateCreateInfo { - sType = VkStructureType.PipelineViewportStateCreateInfo, - viewportCount = 1, - pViewports = &viewport, - scissorCount = 1, - pScissors = &scissor + SType = StructureType.PipelineViewportStateCreateInfo, + ViewportCount = 1, + ScissorCount = 1 }; - var rasterizer = new VkPipelineRasterizationStateCreateInfo + var rasterizer = new PipelineRasterizationStateCreateInfo { - sType = VkStructureType.PipelineRasterizationStateCreateInfo, - polygonMode = VkPolygonMode.Fill, - cullMode = VkCullModeFlags.None, - frontFace = VkFrontFace.Clockwise, - lineWidth = 1.0f + SType = StructureType.PipelineRasterizationStateCreateInfo, + PolygonMode = PolygonMode.Fill, + CullMode = CullModeFlags.None, + FrontFace = FrontFace.Clockwise, + LineWidth = 1.0f }; - var multisampling = new VkPipelineMultisampleStateCreateInfo + var multisampling = new PipelineMultisampleStateCreateInfo { - sType = VkStructureType.PipelineMultisampleStateCreateInfo, - rasterizationSamples = VkSampleCountFlags.Count1, - sampleShadingEnable = false + SType = StructureType.PipelineMultisampleStateCreateInfo, + RasterizationSamples = SampleCountFlags.Count1Bit, + SampleShadingEnable = false }; - var colorBlendAttachment = new VkPipelineColorBlendAttachmentState + var colorBlendAttachment = new PipelineColorBlendAttachmentState { - colorWriteMask = VkColorComponentFlags.R | VkColorComponentFlags.G | VkColorComponentFlags.B | VkColorComponentFlags.A + ColorWriteMask = ColorComponentFlags.RBit | ColorComponentFlags.GBit | ColorComponentFlags.BBit | ColorComponentFlags.ABit }; - var colorBlending = new VkPipelineColorBlendStateCreateInfo + var colorBlending = new PipelineColorBlendStateCreateInfo { - sType = VkStructureType.PipelineColorBlendStateCreateInfo, - attachmentCount = 1, - pAttachments = &colorBlendAttachment + SType = StructureType.PipelineColorBlendStateCreateInfo, + AttachmentCount = 1, + PAttachments = &colorBlendAttachment }; - var dynamicStates = new[] { VkDynamicState.Viewport, VkDynamicState.Scissor }; - VkPipelineDynamicStateCreateInfo dynamicState; - fixed (VkDynamicState* pDynamic = dynamicStates) + var dynamicStates = new[] { DynamicState.Viewport, DynamicState.Scissor }; + PipelineDynamicStateCreateInfo dynamicState; + fixed (DynamicState* pDynamic = dynamicStates) { - dynamicState = new VkPipelineDynamicStateCreateInfo + dynamicState = new PipelineDynamicStateCreateInfo { - sType = VkStructureType.PipelineDynamicStateCreateInfo, - dynamicStateCount = (uint)dynamicStates.Length, - pDynamicStates = pDynamic + SType = StructureType.PipelineDynamicStateCreateInfo, + DynamicStateCount = (uint)dynamicStates.Length, + PDynamicStates = pDynamic }; } - VkPipeline pipeline; - fixed (VkPipelineShaderStageCreateInfo* pStages = stages) + Pipeline pipeline; + fixed (PipelineShaderStageCreateInfo* pStages = stages) { - var createInfo = new VkGraphicsPipelineCreateInfo + var createInfo = new GraphicsPipelineCreateInfo { - sType = VkStructureType.GraphicsPipelineCreateInfo, - stageCount = (uint)stages.Length, - pStages = pStages, - pVertexInputState = &vertexInputInfo, - pInputAssemblyState = &inputAssembly, - pViewportState = &viewportState, - pRasterizationState = &rasterizer, - pMultisampleState = &multisampling, - pColorBlendState = &colorBlending, - pDynamicState = &dynamicState, - layout = Layout, - renderPass = _swapchain.RenderPass, - subpass = 0 + SType = StructureType.GraphicsPipelineCreateInfo, + StageCount = (uint)stages.Length, + PStages = pStages, + PVertexInputState = &vertexInputInfo, + PInputAssemblyState = &inputAssembly, + PViewportState = &viewportState, + PRasterizationState = &rasterizer, + PMultisampleState = &multisampling, + PColorBlendState = &colorBlending, + PDynamicState = &dynamicState, + Layout = Layout, + RenderPass = _swapchain.RenderPass, + Subpass = 0 }; - var result = _context.DeviceApi.vkCreateGraphicsPipelines(VkPipelineCache.Null, 1, &createInfo, null, &pipeline); - if (result != VkResult.Success) + var result = _context.Vk.CreateGraphicsPipelines(_context.Device, default, 1, &createInfo, null, &pipeline); + if (result != Result.Success) throw new InvalidOperationException($"vkCreateGraphicsPipelines failed: {result}"); } - VkStringInterop.Free(stages[0].pName); - VkStringInterop.Free(stages[1].pName); - + SilkMarshal.FreeString(entryName, NativeStringEncoding.UTF8); return pipeline; } public void Dispose() { - _context.DeviceApi.vkDeviceWaitIdle(); - _context.DeviceApi.vkDestroyPipeline(Handle); - _context.DeviceApi.vkDestroyPipelineLayout(Layout); - _context.DeviceApi.vkDestroyShaderModule(_vertexModule); - _context.DeviceApi.vkDestroyShaderModule(_fragmentModule); + _context.Vk.DeviceWaitIdle(_context.Device); + _context.Vk.DestroyPipeline(_context.Device, Handle, null); + _context.Vk.DestroyPipelineLayout(_context.Device, Layout, null); + _context.Vk.DestroyShaderModule(_context.Device, _vertexModule, null); + _context.Vk.DestroyShaderModule(_context.Device, _fragmentModule, null); } }