diff options
Diffstat (limited to 'Backends/VkvgBackend/src/DefaultBackend.cs')
| -rw-r--r-- | Backends/VkvgBackend/src/DefaultBackend.cs | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/Backends/VkvgBackend/src/DefaultBackend.cs b/Backends/VkvgBackend/src/DefaultBackend.cs new file mode 100644 index 00000000..68793806 --- /dev/null +++ b/Backends/VkvgBackend/src/DefaultBackend.cs @@ -0,0 +1,299 @@ +// Copyright (c) 2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using Drawing2D; +using Glfw; +using vke; +using Vulkan; +using static Vulkan.Vk; +using Device = vke.Device; + +namespace Crow.Backends +{ + public class DefaultBackend : IBackend + { + protected Instance instance; + protected PhysicalDevice phy; + protected vke.Device dev; + protected Queue graphicQueue; //for vkvg, we must have at least a graphic queue + protected IntPtr hWin; //GLFW window native pointer. + protected VkSurfaceKHR hSurf; //Vulkan Surface + protected SwapChain swapChain; + protected CommandPool cmdPool; + protected PrimaryCommandBuffer[] cmds; + protected VkSemaphore[] drawComplete; + protected Fence drawFence; + VkvgBackend.Surface surf; + Crow.VkvgBackend.Device vkvgDev; + SampleCount samples = SampleCount.Sample_1; + bool vsync = false; + + bool tryGetPhy (vke.PhysicalDeviceCollection physicalDevices, VkPhysicalDeviceType phyType, out PhysicalDevice phy, bool swapchainSupport) { + phy = physicalDevices.FirstOrDefault (p => p.Properties.deviceType == phyType && p.HasSwapChainSupport == swapchainSupport); + return phy != null; + } + public DefaultBackend (int width, int height) + { +#if DEBUG + instance = new Instance (Ext.I.VK_EXT_debug_utils); +#else + instance = new Instance (); +#endif + vke.PhysicalDeviceCollection phys = instance.GetAvailablePhysicalDevice (); + if (phys.Count() == 0) + throw new Exception ("[Crow.VkvgBackend] No vulkan hardware found."); + + if (!tryGetPhy (phys, VkPhysicalDeviceType.DiscreteGpu, out phy, false)) + if (!tryGetPhy (phys, VkPhysicalDeviceType.IntegratedGpu, out phy, false)) + phy = phys[0]; + + VkPhysicalDeviceFeatures enabledFeatures = default; + dev = new vke.Device (phy); + graphicQueue = new Queue (dev, VkQueueFlags.Graphics); + dev.Activate (enabledFeatures); + + vkvgDev = new Crow.VkvgBackend.Device ( + instance.Handle, phy.Handle, dev.VkDev.Handle, graphicQueue.qFamIndex, samples); + + surf = new VkvgBackend.Surface (vkvgDev, (int)width, (int)height); + } + public DefaultBackend (IntPtr nativeWindoPointer, int width, int height) + { + hWin = nativeWindoPointer; + + SwapChain.IMAGES_USAGE = VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferDst; + SwapChain.PREFERED_FORMAT = VkFormat.B8g8r8a8Unorm; + + List<string> instExts = new List<string> (Glfw3.GetRequiredInstanceExtensions ()); +#if DEBUG + instExts.Add (Ext.I.VK_EXT_debug_utils); +#endif + instance = new Instance (instExts.ToArray()); + hSurf = instance.CreateSurface (hWin); + + vke.PhysicalDeviceCollection phys = instance.GetAvailablePhysicalDevice (); + if (phys.Count() == 0) + throw new Exception ("[Crow.VkvgBackend] No vulkan hardware found."); + + if (!tryGetPhy (phys, VkPhysicalDeviceType.DiscreteGpu, out phy, true)) + if (!tryGetPhy (phys, VkPhysicalDeviceType.IntegratedGpu, out phy, true)) + phy = phys[0]; + + VkPhysicalDeviceFeatures enabledFeatures = default; + dev = new vke.Device (phy); + graphicQueue = new PresentQueue (dev, VkQueueFlags.Graphics, hSurf); + + dev.Activate (enabledFeatures, Ext.D.VK_KHR_swapchain); + + swapChain = new SwapChain (graphicQueue as PresentQueue, (uint)width, (uint)height, SwapChain.PREFERED_FORMAT, + vsync ? VkPresentModeKHR.FifoKHR : VkPresentModeKHR.ImmediateKHR); + swapChain.Activate (); + + /*width = swapChain.Width; + height = swapChain.Height;*/ + + cmdPool = new CommandPool (dev, graphicQueue.qFamIndex, VkCommandPoolCreateFlags.ResetCommandBuffer); + cmds = cmdPool.AllocateCommandBuffer (swapChain.ImageCount); + + drawComplete = new VkSemaphore[swapChain.ImageCount]; + drawFence = new Fence (dev, true, "draw fence"); + + for (int i = 0; i < swapChain.ImageCount; i++) { + drawComplete[i] = dev.CreateSemaphore (); + drawComplete[i].SetDebugMarkerName (dev, "Semaphore DrawComplete" + i); + } + + cmdPool.SetName ("main CmdPool"); + + vkvgDev = new Crow.VkvgBackend.Device ( + instance.Handle, phy.Handle, dev.VkDev.Handle, graphicQueue.qFamIndex, samples); + + createMainSurface ((uint)width, (uint)height); + } + ~DefaultBackend () + { + Dispose (false); + } + public virtual ISurface CreateSurface(int width, int height) + => new Crow.VkvgBackend.Surface (vkvgDev, width, height); + public virtual ISurface CreateSurface(byte[] data, int width, int height) + => new Crow.VkvgBackend.Surface (vkvgDev, data, width, height); + public ISurface MainSurface => surf; + public IRegion CreateRegion () => new Crow.VkvgBackend.Region (); + public IContext CreateContext (ISurface surf) + { + Crow.VkvgBackend.Context gr = new Crow.VkvgBackend.Context (surf); + return gr; + } + //IPattern CreatePattern (PatternType patternType); + public IGradient CreateGradient (GradientType gradientType, Rectangle bounds) + { + switch (gradientType) { + case GradientType.Vertical: + return new Crow.VkvgBackend.LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); + case GradientType.Horizontal: + return new Crow.VkvgBackend.LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); + case GradientType.Oblic: + return new Crow.VkvgBackend.LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); + case GradientType.Radial: + throw new NotImplementedException (); + } + return null; + } + public byte[] LoadBitmap (Stream stream, out Size dimensions) + { + byte[] image; +#if STB_SHARP + StbImageSharp.ImageResult stbi = StbImageSharp.ImageResult.FromStream (stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha); + image = new byte[stbi.Data.Length]; + + Array.Copy (stbi.Data, image, stbi.Data.Length); + dimensions = new Size (stbi.Width, stbi.Height); +#else + using (StbImage stbi = new StbImage (stream)) { + image = new byte [stbi.Size]; + Marshal.Copy (stbi.Handle, image, 0, stbi.Size); + dimensions = new Size (stbi.Width, stbi.Height); + } +#endif + return image; + } + public ISvgHandle LoadSvg(Stream stream) + { + using (BinaryReader sr = new BinaryReader (stream)) + return new VkvgBackend.SvgHandle(vkvgDev, sr.ReadBytes ((int)stream.Length)); + } + public ISvgHandle LoadSvg(string svgFragment) => + new VkvgBackend.SvgHandle (vkvgDev,System.Text.Encoding.Unicode.GetBytes (svgFragment)); + bool disposeContextOnFlush; + IRegion clipping; + protected void clear(IContext ctx) { + for (int i = 0; i < clipping.NumRectangles; i++) + ctx.Rectangle (clipping.GetRectangle (i)); + + ctx.ClipPreserve (); + ctx.Operator = Operator.Clear; + ctx.Fill (); + ctx.Operator = Operator.Over; + } + public IContext PrepareUIFrame(IContext existingContext, IRegion clipping) + { + this.clipping = clipping; + IContext ctx = existingContext; + if (ctx == null) { + disposeContextOnFlush = true; + ctx = new VkvgBackend.Context (MainSurface); + } else + disposeContextOnFlush = false; + + clear (ctx); + + return ctx; + } + public void FlushUIFrame(IContext ctx) + { + if (disposeContextOnFlush) + ctx.Dispose (); + clipping = null; + + dev.WaitIdle(); + + int idx = swapChain.GetNextImage (); + if (idx < 0) { + createMainSurface (swapChain.Width, swapChain.Height); + return; + } + + drawFence.Wait (); + drawFence.Reset (); + + graphicQueue.Submit (cmds[idx], swapChain.presentComplete, drawComplete[idx], drawFence); + (graphicQueue as PresentQueue).Present (swapChain, drawComplete[idx]); + + dev.WaitIdle(); + } + public void ResizeMainSurface (int width, int height) { + //resize is done on swapchain image aquisition failure + } + vke.Image blitSource; + void createMainSurface (uint width, uint height) { + dev.WaitIdle(); + + blitSource?.Dispose (); + surf?.Dispose (); + surf = new VkvgBackend.Surface (vkvgDev, (int)width, (int)height); + + cmdPool.Reset(); + + blitSource = new vke.Image (dev, + new Vulkan.VkImage((ulong)surf.VkImage.ToInt64()), + Vulkan.VkFormat.B8g8r8a8Unorm, + Vulkan.VkImageUsageFlags.TransferSrc | Vulkan.VkImageUsageFlags.TransferDst | Vulkan.VkImageUsageFlags.ColorAttachment, + width, height); + + for (int i = 0; i < swapChain.ImageCount; i++) { + vke.Image blitDest = swapChain.images[i]; + vke.PrimaryCommandBuffer cmd = cmds[i]; + cmd.Start(); + + blitDest.SetLayout (cmd, VkImageAspectFlags.Color, + VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, + VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.Transfer); + + blitSource.SetLayout (cmd, VkImageAspectFlags.Color, + VkImageLayout.ColorAttachmentOptimal, VkImageLayout.TransferSrcOptimal, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.Transfer); + + blitSource.BlitTo (cmd, blitDest, VkFilter.Nearest); + + blitDest.SetLayout (cmd, VkImageAspectFlags.Color, + VkImageLayout.TransferDstOptimal, VkImageLayout.PresentSrcKHR, + VkPipelineStageFlags.Transfer, VkPipelineStageFlags.BottomOfPipe); + + blitSource.SetLayout (cmd, VkImageAspectFlags.Color, + VkImageLayout.TransferSrcOptimal, VkImageLayout.ColorAttachmentOptimal, + VkPipelineStageFlags.Transfer, VkPipelineStageFlags.ColorAttachmentOutput); + + cmd.End (); + } + dev.WaitIdle (); + } + #region IDispose implementation + bool isDisposed; + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + protected virtual void Dispose (bool disposing) + { + if (!isDisposed && disposing) { + dev.WaitIdle (); + + surf.Dispose (); + vkvgDev.Dispose (); + + for (int i = 0; i < swapChain.ImageCount; i++) { + dev.DestroySemaphore (drawComplete[i]); + cmds[i].Free (); + } + drawFence.Dispose (); + swapChain.Dispose (); + cmdPool.Dispose (); + + vkDestroySurfaceKHR (instance.Handle, hSurf, IntPtr.Zero); + + dev.Dispose (); + instance.Dispose (); + } + isDisposed = true; + } + #endregion + } +} |