From: Jean-Philippe Bruyère Date: Thu, 23 Dec 2021 02:33:41 +0000 (+0100) Subject: update backend system, create first backend found in backend directory X-Git-Tag: v1.0.1-beta~2 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=e96195b42a317651e29c80be488ed3778140e914;p=jp%2Fcrow.git update backend system, create first backend found in backend directory --- diff --git a/Backends/CairoBackend/src/CairoBackendBase.cs b/Backends/CairoBackend/src/CairoBackendBase.cs index db6895c2..c7821ee3 100644 --- a/Backends/CairoBackend/src/CairoBackendBase.cs +++ b/Backends/CairoBackend/src/CairoBackendBase.cs @@ -5,14 +5,19 @@ using Glfw; namespace Crow.CairoBackend { - public abstract class CairoBackendBase : IBackend { - protected IntPtr hWin; + public abstract class CairoBackendBase : CrowBackend { + /// + /// Main rendering surface, usualy an accelerated window surface + /// protected Surface surf; + protected IRegion clipping; /// Global font rendering settings for Cairo FontOptions FontRenderingOptions; /// Global font rendering settings for Cairo Antialias Antialias = Antialias.Subpixel; - protected CairoBackendBase () + bool disposeContextOnFlush; + protected CairoBackendBase (int width, int height, IntPtr nativeWindow) + : base (width, height, nativeWindow) { FontRenderingOptions = new FontOptions (); FontRenderingOptions.Antialias = Antialias.Subpixel; @@ -24,11 +29,11 @@ namespace Crow.CairoBackend { Dispose (false); } - public abstract ISurface CreateSurface(int width, int height); - public abstract ISurface CreateSurface(byte[] data, int width, int height); - public ISurface MainSurface => surf; - public IRegion CreateRegion () => new Region (); - public IContext CreateContext (ISurface surf) + public override abstract ISurface CreateSurface(int width, int height); + public override abstract ISurface CreateSurface(byte[] data, int width, int height); + public override ISurface MainSurface => surf; + public override IRegion CreateRegion () => new Region (); + public override IContext CreateContext (ISurface surf) { Context gr = new Context (surf as Surface); gr.FontOptions = FontRenderingOptions; @@ -36,7 +41,7 @@ namespace Crow.CairoBackend return gr; } //IPattern CreatePattern (PatternType patternType); - public IGradient CreateGradient (GradientType gradientType, Rectangle bounds) + public override IGradient CreateGradient (GradientType gradientType, Rectangle bounds) { switch (gradientType) { case GradientType.Vertical: @@ -50,7 +55,7 @@ namespace Crow.CairoBackend } return null; } - public byte[] LoadBitmap (Stream stream, out Size dimensions) + public override byte[] LoadBitmap (Stream stream, out Size dimensions) { byte[] image; #if STB_SHARP @@ -79,15 +84,13 @@ namespace Crow.CairoBackend #endif return image; } - public ISvgHandle LoadSvg(Stream stream) + public override ISvgHandle LoadSvg(Stream stream) { using (BinaryReader sr = new BinaryReader (stream)) return new SvgHandle (sr.ReadBytes ((int)stream.Length)); } - public ISvgHandle LoadSvg(string svgFragment) => + public override ISvgHandle LoadSvg(string svgFragment) => new SvgHandle (System.Text.Encoding.Unicode.GetBytes (svgFragment)); - bool disposeContextOnFlush; - protected IRegion clipping; protected void clear(IContext ctx) { for (int i = 0; i < clipping.NumRectangles; i++) ctx.Rectangle (clipping.GetRectangle (i)); @@ -97,7 +100,7 @@ namespace Crow.CairoBackend ctx.Fill (); ctx.Operator = Operator.Over; } - public virtual IContext PrepareUIFrame(IContext existingContext, IRegion clipping) + public override IContext PrepareUIFrame(IContext existingContext, IRegion clipping) { this.clipping = clipping; IContext ctx = existingContext; @@ -108,20 +111,19 @@ namespace Crow.CairoBackend disposeContextOnFlush = false; return ctx; } - public virtual void FlushUIFrame(IContext ctx) + public override void FlushUIFrame(IContext ctx) { if (disposeContextOnFlush) ctx.Dispose (); clipping = null; } - public virtual void ResizeMainSurface (int width, int height) + public override void ResizeMainSurface (int width, int height) { surf.Resize (width, height); } #region IDispose implementation - bool isDisposed; - public void Dispose () + public override void Dispose () { Dispose (true); GC.SuppressFinalize (this); diff --git a/Backends/CairoBackend/src/DefaultBackend.cs b/Backends/CairoBackend/src/DefaultBackend.cs deleted file mode 100644 index 24d719da..00000000 --- a/Backends/CairoBackend/src/DefaultBackend.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Crow.CairoBackend; - -namespace Crow.Backends -{ - public class DefaultBackend : ImageBackend { - /// - /// Create a new generic backend bound to the application surface - /// - /// backend surface width - /// backend surface height - public DefaultBackend (ref IntPtr nativeWindoPointer, out bool ownGlfwWinHandle, int width, int height) - : base (ref nativeWindoPointer, out ownGlfwWinHandle, width, height) { } - public DefaultBackend (int width, int height) : base (width, height) { - - } - } -} - diff --git a/Backends/CairoBackend/src/EglBackend.cs b/Backends/CairoBackend/src/EglBackend.cs index 38f13a2e..909c3cd9 100644 --- a/Backends/CairoBackend/src/EglBackend.cs +++ b/Backends/CairoBackend/src/EglBackend.cs @@ -12,7 +12,8 @@ namespace Crow.CairoBackend /// /// backend surface width /// backend surface height - public EglBackend (ref IntPtr nativeWindoPointer, out bool ownGlfwWinHandle, int width, int height) : base () { + public EglBackend (int width, int height, IntPtr nativeWindoPointer) + : base (width, height, nativeWindoPointer) { if (nativeWindoPointer == IntPtr.Zero) { Glfw3.Init (); Glfw3.WindowHint (WindowAttribute.ClientApi, Constants.OpenglEsApi); @@ -25,12 +26,6 @@ namespace Crow.CairoBackend hWin = Glfw3.CreateWindow (width, height, "win name", MonitorHandle.Zero, IntPtr.Zero); if (hWin == IntPtr.Zero) throw new Exception ("[GLFW3] Unable to create Window"); - - nativeWindoPointer = hWin; - ownGlfwWinHandle = true; - } else { - hWin = nativeWindoPointer; - ownGlfwWinHandle = false; } Glfw3.MakeContextCurrent (hWin); @@ -44,7 +39,8 @@ namespace Crow.CairoBackend /// /// backend surface width /// backend surface height - public EglBackend (int width, int height) : base () { + public EglBackend (int width, int height) + : base (width, height, IntPtr.Zero) { device = new EGLDevice (Glfw3.GetEGLDisplay (), IntPtr.Zero); surf = new GLTextureSurface (device, width, height); } diff --git a/Backends/CairoBackend/src/ImageBackend.cs b/Backends/CairoBackend/src/ImageBackend.cs index 67512fd9..96899dbd 100644 --- a/Backends/CairoBackend/src/ImageBackend.cs +++ b/Backends/CairoBackend/src/ImageBackend.cs @@ -5,13 +5,15 @@ using Glfw; namespace Crow.CairoBackend { - public class ImageBackend : CairoBackendBase { + public class DefaultBackend : CairoBackendBase { /// /// Create a new generic backend bound to the application surface /// /// backend surface width /// backend surface height - public ImageBackend (ref IntPtr nativeWindoPointer, out bool ownGlfwWinHandle, int width, int height) : base () { + /// + public DefaultBackend (int width, int height, IntPtr nativeWindoPointer) + : base (width, height, nativeWindoPointer) { if (nativeWindoPointer == IntPtr.Zero) { Glfw3.Init (); Glfw3.WindowHint (WindowAttribute.ClientApi, 0); @@ -21,18 +23,11 @@ namespace Crow.CairoBackend hWin = Glfw3.CreateWindow (width, height, "win name", MonitorHandle.Zero, IntPtr.Zero); if (hWin == IntPtr.Zero) throw new Exception ("[GLFW3] Unable to create Window"); - - nativeWindoPointer = hWin; - ownGlfwWinHandle = true; - } else { - hWin = nativeWindoPointer; - ownGlfwWinHandle = false; } - switch (Environment.OSVersion.Platform) { case PlatformID.Unix: IntPtr disp = Glfw3.GetX11Display (); - IntPtr nativeWin = Glfw3.GetX11Window (nativeWindoPointer); + IntPtr nativeWin = Glfw3.GetX11Window (hWin); Int32 scr = Glfw3.GetX11DefaultScreen (disp); IntPtr visual = Glfw3.GetX11DefaultVisual (disp, scr); surf = new XlibSurface (disp, nativeWin, visual, width, height); @@ -40,7 +35,7 @@ namespace Crow.CairoBackend case PlatformID.Win32NT: case PlatformID.Win32S: case PlatformID.Win32Windows: - IntPtr hWin32 = Glfw3.GetWin32Window (nativeWindoPointer); + IntPtr hWin32 = Glfw3.GetWin32Window (hWin); IntPtr hdc = Glfw3.GetWin32DC (hWin32); surf = new Win32Surface (hdc); break; @@ -53,10 +48,12 @@ namespace Crow.CairoBackend /// /// backend surface width /// backend surface height - public ImageBackend (int width, int height) : base () { + public DefaultBackend (int width, int height) + : base (width, height, IntPtr.Zero) { surf = new ImageSurface (Format.ARGB32, width, height); } - public ImageBackend (IntPtr surfaceBitmapData, int width, int height, int stride) : base () { + public DefaultBackend (IntPtr surfaceBitmapData, int width, int height, int stride) + : base (width, height, IntPtr.Zero) { surf = new ImageSurface (surfaceBitmapData, Format.ARGB32, width, height, stride); } diff --git a/Backends/Directory.Build.props b/Backends/Directory.Build.props new file mode 100644 index 00000000..b78ea8c6 --- /dev/null +++ b/Backends/Directory.Build.props @@ -0,0 +1,29 @@ + + + $(SolutionDir)build\backends\$(Configuration)\ + MIT + Jean-Philippe Bruyère + 7.3 + + 0.9.9 + $(CrowVersion)-beta + + + true + + + false + + + false + + + false + + 0.2.14 + + $(NoWarn);1591 + + diff --git a/Backends/SkiaBackend/src/DefaultBackend.cs b/Backends/SkiaBackend/src/DefaultBackend.cs index 21f7e4da..7d86f1fb 100644 --- a/Backends/SkiaBackend/src/DefaultBackend.cs +++ b/Backends/SkiaBackend/src/DefaultBackend.cs @@ -3,15 +3,310 @@ // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; using Crow.SkiaBackend; +using Drawing2D; +using Glfw; +using SkiaSharp; -namespace Crow.Backends +using vke; +using Vulkan; +using static Vulkan.Vk; + +namespace Crow.SkiaBackend { - public class DefaultBackend : VulkanBackend + public class DefaultBackend : CrowBackend { - public DefaultBackend (ref IntPtr nativeWindoPointer, out bool ownGlfwWinHandle, int width, int height) - : base (ref nativeWindoPointer, out ownGlfwWinHandle, width, height) {} + protected Instance instance; + protected PhysicalDevice phy; + protected vke.Device dev; + protected Queue graphicQueue; //for vkvg, we must have at least a graphic queue + protected VkSurfaceKHR hSurf; //Vulkan Surface + protected SwapChain swapChain; + protected CommandPool cmdPool; + protected PrimaryCommandBuffer[] cmds; + protected VkSemaphore[] drawComplete; + protected Fence drawFence; + VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1; + bool vsync = false; + + GRContext gr; + VkSurface surf; + public override ISurface MainSurface => surf; + + 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; + } + + IntPtr getVkProcAddress(string name, IntPtr instance, IntPtr device) { + using (FixedUtf8String n = new FixedUtf8String (name)) + { + return device == IntPtr.Zero ? + Vk.vkGetInstanceProcAddr (instance, n) : + Vk.vkGetDeviceProcAddr (device, n); + } + } + #region CTOR + /// + /// Create a new offscreen backend, used in perfTests + /// + /// backend surface width + /// backend surface height public DefaultBackend (int width, int height) - : base (width, height) {} + : base (width, height, IntPtr.Zero) { +#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); + + } + public DefaultBackend (int width, int height, IntPtr nativeWindoPointer) + : base (width, height, nativeWindoPointer) + { + if (nativeWindoPointer == IntPtr.Zero) { + Glfw3.Init (); + Glfw3.WindowHint (WindowAttribute.ClientApi, 0); + Glfw3.WindowHint (WindowAttribute.Resizable, 1); + Glfw3.WindowHint (WindowAttribute.Decorated, 1); + + hWin = Glfw3.CreateWindow (width, height, "win name", MonitorHandle.Zero, IntPtr.Zero); + if (hWin == IntPtr.Zero) + throw new Exception ("[GLFW3] Unable to create Window"); + } + + SwapChain.IMAGES_USAGE = VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferDst; + SwapChain.PREFERED_FORMAT = VkFormat.B8g8r8a8Srgb; + + List instExts = new List (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 (); + + 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"); + GRVkBackendContext grVkCtx = new GRVkBackendContext { + VkInstance = instance.Handle, + VkPhysicalDevice = phy.Handle, + VkQueue = graphicQueue.Handle.Handle, + VkDevice = dev.Handle.Handle, + GraphicsQueueIndex = 0, + GetProcedureAddress = getVkProcAddress + }; + gr = GRContext.CreateVulkan (grVkCtx); + + createMainSurface ((uint)width, (uint)height); + } + ~DefaultBackend () + { + Dispose (false); + } + #endregion + + public override IContext CreateContext(ISurface surf) => new Context (surf as VkSurface); + public override IGradient CreateGradient(GradientType gradientType, Rectangle bounds) + { + switch (gradientType) { + case GradientType.Vertical: + return new LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); + case GradientType.Horizontal: + return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); + case GradientType.Oblic: + return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); + case GradientType.Radial: + throw new NotImplementedException (); + } + return null; + } + + public override IRegion CreateRegion () => new Crow.SkiaBackend.Region (); + public override ISurface CreateSurface(int width, int height) + => new VkSurface (dev, graphicQueue, gr, (int)width, (int)height, samples); + public override ISurface CreateSurface(byte[] data, int width, int height) + { + throw new NotImplementedException(); + } + + public override byte[] LoadBitmap(Stream stream, out Size dimensions) + { + throw new NotImplementedException(); + } + + public override ISvgHandle LoadSvg(Stream stream) + => new SvgHandle (stream); + + public override ISvgHandle LoadSvg(string svgFragment) + { + throw new NotImplementedException(); + } + + 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 override IContext PrepareUIFrame(IContext existingContext, IRegion clipping) + { + this.clipping = clipping; + IContext ctx = existingContext; + if (ctx == null) { + disposeContextOnFlush = true; + ctx = new Context (surf); + } else + disposeContextOnFlush = false; + + clear (ctx); + + return ctx; + } + public override void FlushUIFrame(IContext ctx) + { + //surf.Canvas.Flush (); + surf.Flush(); + + 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 override void ResizeMainSurface (int width, int height) { + //resize is done on swapchain image aquisition failure + } + void createMainSurface (uint width, uint height) { + dev.WaitIdle(); + + surf?.Dispose (); + surf = new VkSurface (dev, graphicQueue, gr, (int)width, (int)height, samples); + + cmdPool.Reset(); + + vke.Image blitSource = surf.Img; + + 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 IDisposable implementation + public override void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + + protected virtual void Dispose (bool disposing) + { + if (!isDisposed && disposing) { + dev.WaitIdle (); + + surf.Dispose (); + gr.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 } } \ No newline at end of file diff --git a/Backends/SkiaBackend/src/EglBackend.cs b/Backends/SkiaBackend/src/EglBackend.cs index 96be7280..599a7c2b 100644 --- a/Backends/SkiaBackend/src/EglBackend.cs +++ b/Backends/SkiaBackend/src/EglBackend.cs @@ -10,15 +10,15 @@ using SkiaSharp; namespace Crow.SkiaBackend { - public class EglBackend : IBackend + public class EglBackend : CrowBackend { [DllImport("libEGL")] extern static IntPtr eglGetProcAddress(string procname); - int sampleCount = 1; Surface surf; - public ISurface MainSurface => surf; - public EglBackend (IntPtr nativeWindoPointer, int width, int height) : base () { + public override ISurface MainSurface => surf; + public EglBackend (int width, int height, IntPtr nativeWindoPointer) + : base (width, height, nativeWindoPointer) { } /// @@ -26,7 +26,8 @@ namespace Crow.SkiaBackend /// /// backend surface width /// backend surface height - public EglBackend (int width, int height) : base () + public EglBackend (int width, int height) + : base (width, height, IntPtr.Zero) { /*GRGlGetProcedureAddressDelegate del = new GRGlGetProcedureAddressDelegate (eglGetProcAddress); GRGlInterface iface = GRGlInterface.CreateGles (del); @@ -37,62 +38,62 @@ namespace Crow.SkiaBackend SKSurface.CreateAsRenderTarget (gr,) surf = new Surface (width, height);*/ } - public IContext CreateContext(ISurface surf) + public override ISurface CreateSurface(int width, int height) { throw new NotImplementedException(); } - public IGradient CreateGradient(GradientType gradientType, Rectangle bounds) + public override ISurface CreateSurface(byte[] data, int width, int height) { throw new NotImplementedException(); } - public IRegion CreateRegion() + public override IRegion CreateRegion() { throw new NotImplementedException(); } - public ISurface CreateSurface(int width, int height) + public override IContext CreateContext(ISurface surf) { throw new NotImplementedException(); } - public ISurface CreateSurface(byte[] data, int width, int height) + public override IGradient CreateGradient(GradientType gradientType, Rectangle bounds) { throw new NotImplementedException(); } - public void Dispose() + public override byte[] LoadBitmap(Stream stream, out Size dimensions) { throw new NotImplementedException(); } - public void FlushUIFrame(IContext ctx) + public override ISvgHandle LoadSvg(Stream stream) { throw new NotImplementedException(); } - public byte[] LoadBitmap(Stream stream, out Size dimensions) + public override ISvgHandle LoadSvg(string svgFragment) { throw new NotImplementedException(); } - public ISvgHandle LoadSvg(Stream stream) + public override IContext PrepareUIFrame(IContext existingContext, IRegion clipping) { throw new NotImplementedException(); } - public ISvgHandle LoadSvg(string svgFragment) + public override void FlushUIFrame(IContext ctx) { throw new NotImplementedException(); } - public IContext PrepareUIFrame(IContext existingContext, IRegion clipping) + public override void ResizeMainSurface(int width, int height) { throw new NotImplementedException(); } - public void ResizeMainSurface(int width, int height) + public override void Dispose() { throw new NotImplementedException(); } diff --git a/Backends/SkiaBackend/src/VulkanBackend.cs b/Backends/SkiaBackend/src/VulkanBackend.cs deleted file mode 100644 index 09069b77..00000000 --- a/Backends/SkiaBackend/src/VulkanBackend.cs +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright (c) 2021 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using Crow.SkiaBackend; -using Drawing2D; -using Glfw; -using SkiaSharp; - -using vke; -using Vulkan; -using static Vulkan.Vk; - -namespace Crow.SkiaBackend -{ - public class VulkanBackend : 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; - VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1; - bool vsync = false; - - GRContext gr; - VkSurface surf; - public ISurface MainSurface => surf; - - 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; - } - - IntPtr getVkProcAddress(string name, IntPtr instance, IntPtr device) { - using (FixedUtf8String n = new FixedUtf8String (name)) - { - return device == IntPtr.Zero ? - Vk.vkGetInstanceProcAddr (instance, n) : - Vk.vkGetDeviceProcAddr (device, n); - } - } - #region CTOR - /// - /// Create a new offscreen backend, used in perfTests - /// - /// backend surface width - /// backend surface height - public VulkanBackend (int width, int height) : base () { -#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); - - } - public VulkanBackend (ref IntPtr nativeWindoPointer, out bool ownGlfwWinHandle, int width, int height) - : base () - { - if (nativeWindoPointer == IntPtr.Zero) { - Glfw3.Init (); - Glfw3.WindowHint (WindowAttribute.ClientApi, 0); - Glfw3.WindowHint (WindowAttribute.Resizable, 1); - Glfw3.WindowHint (WindowAttribute.Decorated, 1); - - hWin = Glfw3.CreateWindow (width, height, "win name", MonitorHandle.Zero, IntPtr.Zero); - if (hWin == IntPtr.Zero) - throw new Exception ("[GLFW3] Unable to create Window"); - - nativeWindoPointer = hWin; - ownGlfwWinHandle = true; - } else { - hWin = nativeWindoPointer; - ownGlfwWinHandle = false; - } - - SwapChain.IMAGES_USAGE = VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferDst; - SwapChain.PREFERED_FORMAT = VkFormat.B8g8r8a8Srgb; - - List instExts = new List (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 (); - - 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"); - GRVkBackendContext grVkCtx = new GRVkBackendContext { - VkInstance = instance.Handle, - VkPhysicalDevice = phy.Handle, - VkQueue = graphicQueue.Handle.Handle, - VkDevice = dev.VkDev.Handle, - GraphicsQueueIndex = 0, - GetProcedureAddress = getVkProcAddress - }; - gr = GRContext.CreateVulkan (grVkCtx); - - createMainSurface ((uint)width, (uint)height); - } - ~VulkanBackend () - { - Dispose (false); - } - #endregion - - public IContext CreateContext(ISurface surf) => new Context (surf as VkSurface); - public IGradient CreateGradient(GradientType gradientType, Rectangle bounds) - { - switch (gradientType) { - case GradientType.Vertical: - return new LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); - case GradientType.Horizontal: - return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); - case GradientType.Oblic: - return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); - case GradientType.Radial: - throw new NotImplementedException (); - } - return null; - } - - public IRegion CreateRegion () => new Crow.SkiaBackend.Region (); - public ISurface CreateSurface(int width, int height) - => new VkSurface (dev, graphicQueue, gr, (int)width, (int)height, samples); - public ISurface CreateSurface(byte[] data, int width, int height) - { - throw new NotImplementedException(); - } - - public byte[] LoadBitmap(Stream stream, out Size dimensions) - { - throw new NotImplementedException(); - } - - public ISvgHandle LoadSvg(Stream stream) - => new SvgHandle (stream); - - public ISvgHandle LoadSvg(string svgFragment) - { - throw new NotImplementedException(); - } - - 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 Context (surf); - } else - disposeContextOnFlush = false; - - clear (ctx); - - return ctx; - } - public void FlushUIFrame(IContext ctx) - { - //surf.Canvas.Flush (); - surf.Flush(); - - 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 - } - void createMainSurface (uint width, uint height) { - dev.WaitIdle(); - - surf?.Dispose (); - surf = new VkSurface (dev, graphicQueue, gr, (int)width, (int)height, samples); - - cmdPool.Reset(); - - vke.Image blitSource = surf.Img; - - 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 IDisposable implementation - bool isDisposed; - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - } - - protected virtual void Dispose (bool disposing) - { - if (!isDisposed && disposing) { - dev.WaitIdle (); - - surf.Dispose (); - gr.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 - } -} \ No newline at end of file diff --git a/Backends/VkvgBackend/src/DefaultBackend.cs b/Backends/VkvgBackend/src/DefaultBackend.cs index ac7ff1db..5741d064 100644 --- a/Backends/VkvgBackend/src/DefaultBackend.cs +++ b/Backends/VkvgBackend/src/DefaultBackend.cs @@ -16,20 +16,19 @@ using Device = vke.Device; namespace Crow.Backends { - public class DefaultBackend : IBackend + public class DefaultBackend : CrowBackend { 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.Surface surf; Crow.VkvgBackend.Device vkvgDev; SampleCount samples = SampleCount.Sample_1; bool vsync = false; @@ -39,8 +38,10 @@ namespace Crow.Backends return phy != null; } public DefaultBackend (int width, int height) + : base (width, height, IntPtr.Zero) { #if DEBUG + //Instance.RENDER_DOC_CAPTURE = true; instance = new Instance (Ext.I.VK_EXT_debug_utils); #else instance = new Instance (); @@ -61,9 +62,10 @@ namespace Crow.Backends vkvgDev = new Crow.VkvgBackend.Device ( instance.Handle, phy.Handle, dev.VkDev.Handle, graphicQueue.qFamIndex, samples); - surf = new VkvgBackend.Surface (vkvgDev, (int)width, (int)height); + surf = new Crow.VkvgBackend.Surface (vkvgDev, (int)width, (int)height); } - public DefaultBackend (ref IntPtr nativeWindoPointer, out bool ownGlfwWinHandle, int width, int height) + public DefaultBackend (int width, int height, IntPtr nativeWindoPointer) + : base (width, height, nativeWindoPointer) { if (nativeWindoPointer == IntPtr.Zero) { Glfw3.Init (); @@ -75,11 +77,6 @@ namespace Crow.Backends if (hWin == IntPtr.Zero) throw new Exception ("[GLFW3] Unable to create Window"); - nativeWindoPointer = hWin; - ownGlfwWinHandle = true; - } else { - hWin = nativeWindoPointer; - ownGlfwWinHandle = false; } SwapChain.IMAGES_USAGE = VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferDst; @@ -87,6 +84,7 @@ namespace Crow.Backends List instExts = new List (Glfw3.GetRequiredInstanceExtensions ()); #if DEBUG + //Instance.RENDER_DOC_CAPTURE = true; instExts.Add (Ext.I.VK_EXT_debug_utils); #endif instance = new Instance (instExts.ToArray()); @@ -136,25 +134,25 @@ namespace Crow.Backends { Dispose (false); } - public virtual ISurface CreateSurface(int width, int height) + public override ISurface CreateSurface(int width, int height) => new Crow.VkvgBackend.Surface (vkvgDev, width, height); - public virtual ISurface CreateSurface(byte[] data, int width, int height) + public override 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) + public override ISurface MainSurface => surf; + public override IRegion CreateRegion () => new Crow.VkvgBackend.Region (); + public override IContext CreateContext (ISurface surf) { - Crow.VkvgBackend.Context gr = new VkvgBackend.Context (surf as VkvgBackend.Surface); + Crow.VkvgBackend.Context gr = new Crow.VkvgBackend.Context (surf as Crow.VkvgBackend.Surface); return gr; } //IPattern CreatePattern (PatternType patternType); - public IGradient CreateGradient (GradientType gradientType, Rectangle bounds) + public override IGradient CreateGradient (GradientType gradientType, Rectangle bounds) { switch (gradientType) { case GradientType.Vertical: - return new Crow.VkvgBackend.LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); + return new Crow.VkvgBackend.LinearGradient (0, bounds.Top, 0, bounds.Bottom); case GradientType.Horizontal: - return new Crow.VkvgBackend.LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); + return new Crow.VkvgBackend.LinearGradient (bounds.Left, 0, bounds.Right, 0); case GradientType.Oblic: return new Crow.VkvgBackend.LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); case GradientType.Radial: @@ -162,7 +160,7 @@ namespace Crow.Backends } return null; } - public byte[] LoadBitmap (Stream stream, out Size dimensions) + public override byte[] LoadBitmap (Stream stream, out Size dimensions) { byte[] image; #if STB_SHARP @@ -180,13 +178,13 @@ namespace Crow.Backends #endif return image; } - public ISvgHandle LoadSvg(Stream stream) + public override ISvgHandle LoadSvg(Stream stream) { using (BinaryReader sr = new BinaryReader (stream)) - return new VkvgBackend.SvgHandle(vkvgDev, sr.ReadBytes ((int)stream.Length)); + return new Crow.VkvgBackend.SvgHandle(vkvgDev, sr.ReadBytes ((int)stream.Length)); } - public ISvgHandle LoadSvg(string svgFragment) => - new VkvgBackend.SvgHandle (vkvgDev,System.Text.Encoding.Unicode.GetBytes (svgFragment)); + public override ISvgHandle LoadSvg(string svgFragment) => + new Crow.VkvgBackend.SvgHandle (vkvgDev, System.Text.Encoding.Unicode.GetBytes (svgFragment)); bool disposeContextOnFlush; IRegion clipping; protected void clear(IContext ctx) { @@ -198,13 +196,13 @@ namespace Crow.Backends ctx.Fill (); ctx.Operator = Operator.Over; } - public IContext PrepareUIFrame(IContext existingContext, IRegion clipping) + public override IContext PrepareUIFrame(IContext existingContext, IRegion clipping) { this.clipping = clipping; IContext ctx = existingContext; if (ctx == null) { disposeContextOnFlush = true; - ctx = new VkvgBackend.Context (surf); + ctx = new Crow.VkvgBackend.Context (surf); } else disposeContextOnFlush = false; @@ -212,7 +210,7 @@ namespace Crow.Backends return ctx; } - public void FlushUIFrame(IContext ctx) + public override void FlushUIFrame(IContext ctx) { if (disposeContextOnFlush) ctx.Dispose (); @@ -234,7 +232,7 @@ namespace Crow.Backends dev.WaitIdle(); } - public void ResizeMainSurface (int width, int height) { + public override void ResizeMainSurface (int width, int height) { //resize is done on swapchain image aquisition failure } vke.Image blitSource; @@ -243,7 +241,7 @@ namespace Crow.Backends blitSource?.Dispose (); surf?.Dispose (); - surf = new VkvgBackend.Surface (vkvgDev, (int)width, (int)height); + surf = new Crow.VkvgBackend.Surface (vkvgDev, (int)width, (int)height); cmdPool.Reset(); @@ -281,8 +279,7 @@ namespace Crow.Backends dev.WaitIdle (); } #region IDispose implementation - bool isDisposed; - public void Dispose () + public override void Dispose () { Dispose (true); GC.SuppressFinalize (this); diff --git a/Backends/VkvgBackend/src/Device.cs b/Backends/VkvgBackend/src/Device.cs index 82cad101..24d52c73 100644 --- a/Backends/VkvgBackend/src/Device.cs +++ b/Backends/VkvgBackend/src/Device.cs @@ -16,7 +16,7 @@ namespace Crow.VkvgBackend #region CTORS & DTOR public Device (IntPtr instance, IntPtr phy, IntPtr dev, uint qFamIdx, SampleCount samples = SampleCount.Sample_1, uint qIndex = 0) { - handle = NativeMethods.vkvg_device_create_multisample (instance, phy, dev, qFamIdx, qIndex, samples, false); + handle = NativeMethods.vkvg_device_create_from_vk_multisample (instance, phy, dev, qFamIdx, qIndex, samples, false); } ~Device () { diff --git a/Backends/VkvgBackend/src/NativeMethods.cs b/Backends/VkvgBackend/src/NativeMethods.cs index fcaf4620..7b434b5d 100644 --- a/Backends/VkvgBackend/src/NativeMethods.cs +++ b/Backends/VkvgBackend/src/NativeMethods.cs @@ -14,11 +14,13 @@ namespace Crow.VkvgBackend #region Device [DllImport(libvkvg, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr vkvg_device_create(IntPtr instance, IntPtr phy, IntPtr dev, uint qFamIdx, uint qIndex); + internal static extern IntPtr vkvg_device_create(SampleCount samples, bool deferredResolve); [DllImport(libvkvg, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr vkvg_device_destroy(IntPtr device); + internal static extern IntPtr vkvg_device_create_from_vk(IntPtr instance, IntPtr phy, IntPtr dev, uint qFamIdx, uint qIndex); + [DllImport(libvkvg, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr vkvg_device_create_from_vk_multisample(IntPtr inst, IntPtr phy, IntPtr vkdev, uint qFamIdx, uint qIndex, SampleCount samples, bool deferredResolve); [DllImport(libvkvg, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr vkvg_device_create_multisample(IntPtr inst, IntPtr phy, IntPtr vkdev, uint qFamIdx, uint qIndex, SampleCount samples, bool deferredResolve); + internal static extern IntPtr vkvg_device_destroy(IntPtr device); [DllImport(libvkvg, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr vkvg_device_reference(IntPtr dev); [DllImport(libvkvg, CallingConvention = CallingConvention.Cdecl)] diff --git a/Backends/VkvgBackend/src/SvgHandle.cs b/Backends/VkvgBackend/src/SvgHandle.cs index 934483f5..0a6f3007 100644 --- a/Backends/VkvgBackend/src/SvgHandle.cs +++ b/Backends/VkvgBackend/src/SvgHandle.cs @@ -26,10 +26,10 @@ namespace Crow.VkvgBackend } #endregion - public void Render(IContext cr) => - NativeMethods.vkvg_render_svg((cr as Context).handle, handle, null); - public void Render (IContext cr, string id) => - NativeMethods.vkvg_render_svg((cr as Context).handle, handle, id); + public void Render(IContext cr) {} + //NativeMethods.vkvg_render_svg((cr as Context).handle, handle, null); + public void Render (IContext cr, string id) {} + //NativeMethods.vkvg_render_svg((cr as Context).handle, handle, id); public Size Dimensions { get { NativeMethods.nsvg_get_size (handle, out int w, out int h); diff --git a/Backends/VkvgBackend/src/VulkanContext.cs b/Backends/VkvgBackend/src/VulkanContext.cs index 610faa71..2f2134e8 100644 --- a/Backends/VkvgBackend/src/VulkanContext.cs +++ b/Backends/VkvgBackend/src/VulkanContext.cs @@ -145,10 +145,10 @@ namespace Crow.Drawing { this.hWin = hWin; SwapChain.IMAGES_USAGE = VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferDst; - SwapChain.PREFERED_FORMAT = VkFormat.B8g8r8a8Unorm; + SwapChain.PREFERED_FORMAT = VkFormat.B8g8r8a8Srgb; //Instance.VALIDATION = true; - //Instance.RENDER_DOC_CAPTURE = true; + Instance.RENDER_DOC_CAPTURE = true; List instExts = new List (Glfw3.GetRequiredInstanceExtensions ()); #if DEBUG instExts.Add (Ext.I.VK_EXT_debug_utils); @@ -209,7 +209,7 @@ namespace Crow.Drawing { //Console.WriteLine ($"build blit w:{width} h:{height}"); cmdPool.Reset(); - blitSource = new vke.Image (dev, new VkImage((ulong)surf.VkImage.ToInt64()), Vulkan.VkFormat.B8g8r8a8Unorm, + blitSource = new vke.Image (dev, new VkImage((ulong)surf.VkImage.ToInt64()), Vulkan.VkFormat.B8g8r8a8Srgb, Vulkan.VkImageUsageFlags.TransferSrc | Vulkan.VkImageUsageFlags.TransferDst | Vulkan.VkImageUsageFlags.ColorAttachment, width, height); diff --git a/Crow/Crow.csproj b/Crow/Crow.csproj index 707e5642..9a4b1b00 100644 --- a/Crow/Crow.csproj +++ b/Crow/Crow.csproj @@ -72,15 +72,10 @@ - - - - - - - $(DefineConstants);STB_SHARP - + + + diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index 74d0ed94..ac0e20ba 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -153,12 +153,69 @@ namespace Crow } return IntPtr.Zero; } + static string[] backends = {"CairoBackend", "VkvgBackend", "SkiaBackend"}; + static Assembly resolving (System.Runtime.Loader.AssemblyLoadContext ctx, AssemblyName name) { + if (name.Name == "CrowBackend") { + string bp = Path.GetDirectoryName (Assembly.GetExecutingAssembly().Location); + foreach (string b in backends) { + string bPath = Path.Combine (bp, b + ".dll"); + if (File.Exists (bPath)) + return ctx.LoadFromAssemblyPath (bPath); + } + } + return ctx.LoadFromAssemblyName (name); + } + public static BackendType PreferedBackendType = BackendType.Default; + /// + /// If not null, backends assemblies will be search in this directory, else + /// backends are search where the main crow assembly is. + /// + public static string BackendsDirectory = null; + protected static bool tryFindBackendType (out Type backendType) { + backendType = default; + System.Runtime.Loader.AssemblyLoadContext ldCtx = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); + foreach (Assembly a in ldCtx.Assemblies.Where (asb => backends.Contains (asb.GetName ().Name))) { + IEnumerable backendTypes = a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract); + if (backendTypes != null) { + backendType = backendTypes.FirstOrDefault(); + return true; + } + } + string bp = + (!string.IsNullOrEmpty(BackendsDirectory) && Directory.Exists(BackendsDirectory)) ? + BackendsDirectory : Path.GetDirectoryName (Assembly.GetExecutingAssembly().Location); + foreach (string b in backends) { + string bPath = Path.Combine (bp, b + ".dll"); + if (File.Exists (bPath)) { + Assembly a = ldCtx.LoadFromAssemblyPath (bPath); + IEnumerable backendTypes = a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract); + if (backendTypes != null) { + if (PreferedBackendType == BackendType.Default) + backendType = backendTypes.FirstOrDefault(be => be.Name == "DefaultBackend"); + else if (PreferedBackendType == BackendType.Egl) + backendType = backendTypes.FirstOrDefault(be => be.Name == "EglBackend"); + else if (PreferedBackendType == BackendType.Vulkan) + backendType = backendTypes.FirstOrDefault(be => be.Name == "VulkanBackend"); + else if (PreferedBackendType == BackendType.Gl) + backendType = backendTypes.FirstOrDefault(be => be.Name == "GlBackend"); + + if (backendType == null) + backendType = backendTypes.FirstOrDefault(); + return backendType != null; + } + } + } + + return false; + } #region CTOR static Interface () { - System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).ResolvingUnmanagedDll += resolveUnmanaged; + System.Runtime.Loader.AssemblyLoadContext ldCtx = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); + ldCtx.ResolvingUnmanagedDll += resolveUnmanaged; + ldCtx.Resolving += resolving; CROW_CONFIG_ROOT = System.IO.Path.Combine ( @@ -302,7 +359,7 @@ namespace Crow /// True if GLFW window has been created by the backend and should be disposed with the `Interface`, false otherwise. /// protected bool ownWindow; - protected IBackend backend;//backend device + protected CrowBackend backend;//backend device /// Clipping rectangles on the root context protected IRegion clipping; static string backendDeviceTypeString = "Crow.CairoBackend.Device"; @@ -316,7 +373,7 @@ namespace Crow /// Native GLFW window handle bound to this interface. /// public IntPtr WindowHandle => hWin; - public IBackend Backend => backend; + public CrowBackend Backend => backend; /// Main backend surface public ISurface MainSurface => backend.MainSurface; public IntPtr SurfacePointer { @@ -394,7 +451,11 @@ namespace Crow } protected virtual void initBackend () { - backend = new Crow.Backends.DefaultBackend (ref hWin, out ownWindow, clientRectangle.Width, clientRectangle.Height); + if (!tryFindBackendType (out Type backendType)) + throw new Exception ("No backend found."); + backend = (CrowBackend)Activator.CreateInstance (backendType, new object[] {clientRectangle.Width, clientRectangle.Height, hWin}); + hWin = backend.hWin; + ownWindow = backend.ownGlfwWinHandle; clipping = Backend.CreateRegion (); registerGlfwCallbacks (); } diff --git a/Drawing2D/Drawing2D.csproj b/Drawing2D/Drawing2D.csproj index bd40f7a5..f77f3b43 100644 --- a/Drawing2D/Drawing2D.csproj +++ b/Drawing2D/Drawing2D.csproj @@ -21,6 +21,7 @@ Copyright 2022 True true + $(NoWarn);1591 diff --git a/Drawing2D/src/CrowBackend.cs b/Drawing2D/src/CrowBackend.cs new file mode 100644 index 00000000..876632ff --- /dev/null +++ b/Drawing2D/src/CrowBackend.cs @@ -0,0 +1,66 @@ +// Copyright (c) 2018-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.IO; + +namespace Drawing2D +{ + public abstract class CrowBackend: IDisposable + { + /// + /// GLFW Native window pointer + /// + public IntPtr hWin { get; protected set; } + public bool ownGlfwWinHandle { get; private set; } + public CrowBackend (int width, int height, IntPtr nativeWindow = default) { + ownGlfwWinHandle = (nativeWindow == IntPtr.Zero); + hWin = nativeWindow; + } + /// + /// Main rendering surface, usualy an accelerated window surface + /// + public abstract ISurface MainSurface { get; } + /// + /// Create a new surface + /// + /// width of the new surface + /// height of the new surface + /// the new surface instance + public abstract ISurface CreateSurface (int width, int height); + /// + /// Create a new surface backed by the byte array provided as argument + /// + /// a byte array to hold the pixels of the new surface + /// width of the new surface + /// height of the new surface + /// the new surface instance + public abstract ISurface CreateSurface (byte[] data, int width, int height); + //ISurface CreateMainSurface (IntPtr glfwWinHandle, int width, int height); + public abstract IRegion CreateRegion (); + public abstract IContext CreateContext (ISurface surf); + //IPattern CreatePattern (PatternType patternType); + public abstract IGradient CreateGradient (GradientType gradientType, Rectangle bounds); + public abstract byte[] LoadBitmap (Stream stream, out Size dimensions); + public abstract ISvgHandle LoadSvg (Stream stream); + public abstract ISvgHandle LoadSvg (string svgFragment); + public abstract IContext PrepareUIFrame (IContext existingContext, IRegion clipping); + public abstract void FlushUIFrame (IContext ctx); + public abstract void ResizeMainSurface (int width, int height); + + protected bool isDisposed; + public abstract void Dispose(); + /*IRegion CreateRegion (); +ISurface CreateSurface (int width, int height); +ISurface CreateSurface (byte[] data, int width, int height); +ISurface CreateSurface (IntPtr glfwWinHandle, int width, int height); +IContext CreateContext (ISurface surf); +//IPattern CreatePattern (PatternType patternType); +IGradient CreateGradient (GradientType gradientType, Rectangle bounds); +byte[] LoadBitmap (Stream stream, out Size dimensions); +ISvgHandle LoadSvg (Stream stream); +ISvgHandle LoadSvg (string svgFragment);*/ + } +} + diff --git a/Drawing2D/src/Enums.cs b/Drawing2D/src/Enums.cs index bf20a432..b75cc696 100644 --- a/Drawing2D/src/Enums.cs +++ b/Drawing2D/src/Enums.cs @@ -7,9 +7,9 @@ using System; namespace Drawing2D { public enum BackendType { - Native, - OpenGL, - EGL, + Default, + Gl, + Egl, Vulkan } public enum Status diff --git a/Drawing2D/src/IBackend.cs b/Drawing2D/src/IBackend.cs deleted file mode 100644 index ac527162..00000000 --- a/Drawing2D/src/IBackend.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2018-2021 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using System.IO; - -namespace Drawing2D -{ - public interface IBackend: IDisposable - { - ISurface CreateSurface (int width, int height); - ISurface CreateSurface (byte[] data, int width, int height); - //ISurface CreateMainSurface (IntPtr glfwWinHandle, int width, int height); - ISurface MainSurface { get; } - IRegion CreateRegion (); - IContext CreateContext (ISurface surf); - //IPattern CreatePattern (PatternType patternType); - IGradient CreateGradient (GradientType gradientType, Rectangle bounds); - byte[] LoadBitmap (Stream stream, out Size dimensions); - ISvgHandle LoadSvg (Stream stream); - ISvgHandle LoadSvg (string svgFragment); - IContext PrepareUIFrame (IContext existingContext, IRegion clipping); - void FlushUIFrame (IContext ctx); - void ResizeMainSurface (int width, int height); - /*IRegion CreateRegion (); - ISurface CreateSurface (int width, int height); - ISurface CreateSurface (byte[] data, int width, int height); - ISurface CreateSurface (IntPtr glfwWinHandle, int width, int height); - IContext CreateContext (ISurface surf); - //IPattern CreatePattern (PatternType patternType); - IGradient CreateGradient (GradientType gradientType, Rectangle bounds); - byte[] LoadBitmap (Stream stream, out Size dimensions); - ISvgHandle LoadSvg (Stream stream); - ISvgHandle LoadSvg (string svgFragment);*/ - } -} - diff --git a/Samples/Directory.Build.props b/Samples/Directory.Build.props index 1fa6dd16..da64b9b8 100644 --- a/Samples/Directory.Build.props +++ b/Samples/Directory.Build.props @@ -20,6 +20,12 @@ + + + + + + PreserveNewest diff --git a/Samples/PerfTests/Program.cs b/Samples/PerfTests/Program.cs index b1c6d950..ece49f6b 100644 --- a/Samples/PerfTests/Program.cs +++ b/Samples/PerfTests/Program.cs @@ -48,8 +48,8 @@ namespace PerfTests Console.WriteLine ("Usage: PerfTests.exe [options]\n"); Console.WriteLine ("-o,--output:\n\tWrite results to output directory, if omited, results are printed to screen."); Console.WriteLine ("-i,--input:\n\tInput directory to search recursively for '.crow' file to test. If ommitted, builtin unit tests are performs"); - Console.WriteLine ("-w,--width:\n\toutput surface width, not displayed on screen."); - Console.WriteLine ("-h,--height:\n\toutput surface height, not displayed on screen."); + Console.WriteLine ("-x,--width:\n\toutput surface width, not displayed on screen."); + Console.WriteLine ("-y,--height:\n\toutput surface height, not displayed on screen."); Console.WriteLine ("-c,--count:\n\trepeat each test 'c' times. (default = 10, minimum = 5"); Console.WriteLine ("-b,--begin:\n\tStarting stage for measures, may be the stage name or stage index"); @@ -60,7 +60,7 @@ namespace PerfTests Console.WriteLine ("-r,--reset:\n\tenable clear iterators after each test file."); Console.WriteLine ("-u,--update:\n\tmeasure 'n' update cycle with elapsed ticks string notified. (default = 0)"); Console.WriteLine ("-s,--screen:\n\tenable output to screen."); - Console.WriteLine ("--help:\n\tthis help message."); + Console.WriteLine ("-h,--help:\n\tthis help message."); } public TestInterface (string[] args, int width = 800, int height = 600) @@ -84,11 +84,11 @@ namespace PerfTests case "--count": count = Math.Max(5, int.Parse (args[i++])); break; - case "-w": + case "-x": case "--width": clientRectangle.Width = int.Parse (args[i++]); break; - case "-h": + case "-y": case "--height": clientRectangle.Height = int.Parse (args[i++]); break; @@ -110,6 +110,7 @@ namespace PerfTests case "--screen": screenOutput = true; break; + case "-h": case "--help": default: throw new Exception ("none"); @@ -157,11 +158,15 @@ namespace PerfTests } protected override void initBackend() { + if (!tryFindBackendType (out Type backendType)) + throw new Exception ("No backend found."); if (screenOutput) - backend = new Crow.Backends.DefaultBackend (ref hWin, out ownWindow, clientRectangle.Width, clientRectangle.Height); + backend = (Drawing2D.CrowBackend)Activator.CreateInstance (backendType, new object[] {clientRectangle.Width, clientRectangle.Height, hWin}); else - backend = new Crow.Backends.DefaultBackend (clientRectangle.Width, clientRectangle.Height); + backend = (Drawing2D.CrowBackend)Activator.CreateInstance (backendType, new object[] {clientRectangle.Width, clientRectangle.Height}); + hWin = backend.hWin; + ownWindow = backend.ownGlfwWinHandle; clipping = Backend.CreateRegion (); }