From 20de4f3f7291c59a3d22a88f6934b2aaa3f7bb8f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Thu, 25 Nov 2021 04:11:52 +0100 Subject: [PATCH] vkvg backend nearly ok, refresh problem --- Backends/CairoBackend/CairoBackend.csproj | 3 +- Backends/CairoBackend/src/EGLDevice.cs | 5 +- Backends/CairoBackend/src/GLDevice.cs | 12 + Backends/CairoBackend/src/ImageBackend.cs | 6 +- Backends/VkvgBackend/src/Context.cs | 4 +- Backends/VkvgBackend/src/DefaultBackend.cs | 299 +++++++++++++++++++++ Backends/VkvgBackend/src/Device.cs | 52 ---- Backends/VkvgBackend/src/Surface.cs | 7 +- Crow/Crow.csproj | 3 +- Crow/src/Interface.cs | 9 +- Drawing2D/src/IBackend.cs | 1 + Samples/Directory.Build.props | 2 +- Samples/PerfTests/Program.cs | 4 +- 13 files changed, 335 insertions(+), 72 deletions(-) create mode 100644 Backends/VkvgBackend/src/DefaultBackend.cs diff --git a/Backends/CairoBackend/CairoBackend.csproj b/Backends/CairoBackend/CairoBackend.csproj index bc11bc47..986ad859 100644 --- a/Backends/CairoBackend/CairoBackend.csproj +++ b/Backends/CairoBackend/CairoBackend.csproj @@ -6,11 +6,12 @@ - + + diff --git a/Backends/CairoBackend/src/EGLDevice.cs b/Backends/CairoBackend/src/EGLDevice.cs index d9296b74..ef3d5542 100644 --- a/Backends/CairoBackend/src/EGLDevice.cs +++ b/Backends/CairoBackend/src/EGLDevice.cs @@ -28,7 +28,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; -using Drawing2D; namespace Crow.CairoBackend { @@ -39,9 +38,7 @@ namespace Crow.CairoBackend { SetThreadAware(threadAwayre); } - /*public override ISurface CreateSurface(int width, int height) - => new ImageSurface (Format.ARGB32, width, height); - public override ISurface CreateSurface (IntPtr nativeWindoPointer, int width, int height) { + /*public override ISurface CreateSurface (IntPtr nativeWindoPointer, int width, int height) { return new GLSurface (this, Glfw.Glfw3.GetEGLSurface (nativeWindoPointer), width, height); }*/ diff --git a/Backends/CairoBackend/src/GLDevice.cs b/Backends/CairoBackend/src/GLDevice.cs index c7992b74..d90fe1a0 100644 --- a/Backends/CairoBackend/src/GLDevice.cs +++ b/Backends/CairoBackend/src/GLDevice.cs @@ -2,6 +2,9 @@ // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; +using Drawing2D; +using OpenGL; +using static OpenGL.Gl; namespace Crow.CairoBackend { @@ -11,6 +14,15 @@ namespace Crow.CairoBackend public void SetThreadAware (bool value) { NativeMethods.cairo_gl_device_set_thread_aware (handle, value ? 1 : 0); } + public virtual ISurface CreateSurface(int width, int height) { + uint tex = GenTexture (); + BindTexture (TextureTarget.Texture2d, tex); + TexImage2D (TextureTarget.Texture2d, 0, InternalFormat.Rgb, width, height, + 0, PixelFormat.Rgb, PixelType.UnsignedByte, 0); + TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMagFilter, NEAREST); + TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMinFilter, NEAREST); + return new GLSurface (this, Content.ColorAlpha, tex, width, height); + } } } diff --git a/Backends/CairoBackend/src/ImageBackend.cs b/Backends/CairoBackend/src/ImageBackend.cs index e390ca98..7443b787 100644 --- a/Backends/CairoBackend/src/ImageBackend.cs +++ b/Backends/CairoBackend/src/ImageBackend.cs @@ -156,8 +156,10 @@ namespace Crow.CairoBackend ctx.Dispose (); clipping = null; } - - + public void ResizeMainSurface (int width, int height) + { + surf.Resize (width, height); + } #region IDispose implementation bool isDisposed; public void Dispose () diff --git a/Backends/VkvgBackend/src/Context.cs b/Backends/VkvgBackend/src/Context.cs index 20a3cdaa..f303b09f 100644 --- a/Backends/VkvgBackend/src/Context.cs +++ b/Backends/VkvgBackend/src/Context.cs @@ -14,7 +14,7 @@ namespace Crow.VkvgBackend IntPtr handle = IntPtr.Zero; - public Context(Surface surf) + public Context (ISurface surf) { handle = NativeMethods.vkvg_create(surf.Handle); this.FillRule = FillRule.NonZero; @@ -310,7 +310,7 @@ namespace Crow.VkvgBackend public void SelectFontFace(string family, FontSlant slant, FontWeight weight) { - throw new NotImplementedException(); + NativeMethods.vkvg_select_font_face (handle, family); } internal static byte[] TerminateUtf8(string s) 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 +// +// 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 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 (); + + /*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 + } +} diff --git a/Backends/VkvgBackend/src/Device.cs b/Backends/VkvgBackend/src/Device.cs index c2bd40b2..82cad101 100644 --- a/Backends/VkvgBackend/src/Device.cs +++ b/Backends/VkvgBackend/src/Device.cs @@ -32,58 +32,6 @@ namespace Crow.VkvgBackend #region IDevice implementation public void GetDpy (out int hdpy, out int vdpy) => NativeMethods.vkvg_device_get_dpy (handle, out hdpy, out vdpy); public void SetDpy (int hdpy, int vdpy) => NativeMethods.vkvg_device_set_dpy (handle, hdpy, vdpy); - - public IRegion CreateRegion() => new Region (); - - public ISurface CreateSurface(int width, int height) - { - throw new NotImplementedException(); - } - - public ISurface CreateSurface(byte[] data, int width, int height) - { - throw new NotImplementedException(); - } - public ISurface CreateSurface(IntPtr glfwWinHandle, int width, int height) - { - throw new NotImplementedException(); - } - public IContext CreateContext(ISurface surf) - { - throw new NotImplementedException(); - } - - public IGradient CreateGradient(GradientType gradientType, Rectangle bounds) - { - throw new NotImplementedException(); - } - 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) - { - throw new NotImplementedException(); - } - - public ISvgHandle LoadSvg(string svgFragment) - { - throw new NotImplementedException(); - } #endregion diff --git a/Backends/VkvgBackend/src/Surface.cs b/Backends/VkvgBackend/src/Surface.cs index 1c04b204..b32feef6 100644 --- a/Backends/VkvgBackend/src/Surface.cs +++ b/Backends/VkvgBackend/src/Surface.cs @@ -36,9 +36,9 @@ namespace Crow.VkvgBackend AddReference (); } - Surface (IntPtr devHandle, int width, int heigth) + Surface (IntPtr devHandle, int width, int height) { - handle = NativeMethods.vkvg_surface_create (devHandle, (uint)width, (uint)heigth); + handle = NativeMethods.vkvg_surface_create (devHandle, (uint)width, (uint)height); } #endregion ~Surface () @@ -58,7 +58,8 @@ namespace Crow.VkvgBackend public void Resize(int width, int height) { - throw new NotImplementedException(); + NativeMethods.vkvg_surface_destroy (handle); + handle = NativeMethods.vkvg_surface_create (vkvgDev.Handle, (uint)width, (uint)height); } public void Flush () { diff --git a/Crow/Crow.csproj b/Crow/Crow.csproj index ffe4e866..97866d71 100644 --- a/Crow/Crow.csproj +++ b/Crow/Crow.csproj @@ -74,7 +74,8 @@ - + + diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index 19d12f3f..a3870a3a 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -402,7 +402,7 @@ namespace Crow } protected virtual void initBackend () { - backend = new Crow.CairoBackend.ImageBackend (hWin, clientRectangle.Width, clientRectangle.Height); + backend = new Crow.Backends.DefaultBackend (hWin, clientRectangle.Width, clientRectangle.Height); clipping = Backend.CreateRegion (); } /// @@ -412,8 +412,9 @@ namespace Crow { Glfw3.Init (); - Glfw3.WindowHint (WindowAttribute.ClientApi, Constants.OpenglEsApi); - Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 3); + //Glfw3.WindowHint (WindowAttribute.ClientApi, Constants.OpenglEsApi); + Glfw3.WindowHint (WindowAttribute.ClientApi, 0); + //Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 3); //Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 0); Glfw3.WindowHint (WindowAttribute.ContextCreationApi, Constants.EglContextApi); @@ -1431,7 +1432,7 @@ namespace Crow public virtual void ProcessResize(Rectangle bounds){ lock (UpdateMutex) { clientRectangle = bounds; - MainSurface.Resize (clientRectangle.Width, clientRectangle.Height); + backend.ResizeMainSurface (clientRectangle.Width, clientRectangle.Height); foreach (Widget g in GraphicTree) g.RegisterForLayouting (LayoutingType.All); diff --git a/Drawing2D/src/IBackend.cs b/Drawing2D/src/IBackend.cs index 811b95d5..ac527162 100644 --- a/Drawing2D/src/IBackend.cs +++ b/Drawing2D/src/IBackend.cs @@ -22,6 +22,7 @@ namespace Drawing2D 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); diff --git a/Samples/Directory.Build.props b/Samples/Directory.Build.props index 34459547..1fa6dd16 100644 --- a/Samples/Directory.Build.props +++ b/Samples/Directory.Build.props @@ -2,7 +2,7 @@ netcoreapp3.1 - WinExe + Exe $(MSBuildThisFileDirectory)..\ $(SolutionDir)build\$(Configuration)\ diff --git a/Samples/PerfTests/Program.cs b/Samples/PerfTests/Program.cs index cbe62738..d7797496 100644 --- a/Samples/PerfTests/Program.cs +++ b/Samples/PerfTests/Program.cs @@ -161,9 +161,9 @@ namespace PerfTests protected override void initBackend() { if (screenOutput) - backend = new Crow.CairoBackend.ImageBackend (WindowHandle, clientRectangle.Width, clientRectangle.Height); + backend = new Crow.Backends.DefaultBackend (WindowHandle, clientRectangle.Width, clientRectangle.Height); else - backend = new Crow.CairoBackend.ImageBackend (clientRectangle.Width, clientRectangle.Height); + backend = new Crow.Backends.DefaultBackend (clientRectangle.Width, clientRectangle.Height); clipping = Backend.CreateRegion (); } -- 2.47.3