From 8fec3bb113c2616099b5d5a7137f14ba9b88b91e Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 17 Nov 2018 13:33:52 +0300 Subject: [PATCH 1/3] EGL refactoring and locks --- src/Avalonia.Native/GlPlatformFeature.cs | 4 +- src/Avalonia.OpenGL/EglContext.cs | 44 +++++++++++++++ src/Avalonia.OpenGL/EglDisplay.cs | 53 ++----------------- src/Avalonia.OpenGL/EglGlPlatformFeature.cs | 4 +- src/Avalonia.OpenGL/EglGlPlatformSurface.cs | 32 +++++++---- src/Avalonia.OpenGL/EglSurface.cs | 28 ++++++++++ src/Avalonia.OpenGL/IGlContext.cs | 4 +- src/Avalonia.OpenGL/IGlSurface.cs | 10 ---- .../Avalonia.Skia/PlatformRenderInterface.cs | 2 +- 9 files changed, 106 insertions(+), 75 deletions(-) create mode 100644 src/Avalonia.OpenGL/EglContext.cs create mode 100644 src/Avalonia.OpenGL/EglSurface.cs delete mode 100644 src/Avalonia.OpenGL/IGlSurface.cs diff --git a/src/Avalonia.Native/GlPlatformFeature.cs b/src/Avalonia.Native/GlPlatformFeature.cs index 97785e4a81..e39680bdee 100644 --- a/src/Avalonia.Native/GlPlatformFeature.cs +++ b/src/Avalonia.Native/GlPlatformFeature.cs @@ -58,10 +58,8 @@ namespace Avalonia.Native public IGlDisplay Display { get; } - public void MakeCurrent(IGlSurface surface) + public void MakeCurrent() { - if (surface != null) - throw new ArgumentException(nameof(surface)); Context.MakeCurrent(); } } diff --git a/src/Avalonia.OpenGL/EglContext.cs b/src/Avalonia.OpenGL/EglContext.cs new file mode 100644 index 0000000000..27b1abe411 --- /dev/null +++ b/src/Avalonia.OpenGL/EglContext.cs @@ -0,0 +1,44 @@ +using System; +using System.Reactive.Disposables; +using System.Threading; + +namespace Avalonia.OpenGL +{ + public class EglContext : IGlContext + { + private readonly EglDisplay _disp; + private readonly EglInterface _egl; + private readonly object _lock = new object(); + + public EglContext(EglDisplay display, EglInterface egl, IntPtr ctx, IntPtr offscreenSurface) + { + _disp = display; + _egl = egl; + Context = ctx; + OffscreenSurface = offscreenSurface; + } + + public IntPtr Context { get; } + public IntPtr OffscreenSurface { get; } + public IGlDisplay Display => _disp; + + public IDisposable Lock() + { + Monitor.Enter(_lock); + return Disposable.Create(() => Monitor.Exit(_lock)); + } + + public void MakeCurrent() + { + if (!_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, Context)) + throw new OpenGlException("eglMakeCurrent failed"); + } + + public void MakeCurrent(EglSurface surface) + { + var surf = ((EglSurface)surface)?.DangerousGetHandle() ?? OffscreenSurface; + if (!_egl.MakeCurrent(_disp.Handle, surf, surf, Context)) + throw new OpenGlException("eglMakeCurrent failed"); + } + } +} diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs index ea0a9bf087..90a70adcb7 100644 --- a/src/Avalonia.OpenGL/EglDisplay.cs +++ b/src/Avalonia.OpenGL/EglDisplay.cs @@ -12,7 +12,8 @@ namespace Avalonia.OpenGL private readonly IntPtr _display; private readonly IntPtr _config; private readonly int[] _contextAttributes; - + + public IntPtr Handle => _display; public EglDisplay(EglInterface egl) { _egl = egl; @@ -121,7 +122,7 @@ namespace Avalonia.OpenGL }); if (surf == IntPtr.Zero) throw new OpenGlException("eglCreatePbufferSurface failed"); - var rv = new EglContext(this, ctx, surf); + var rv = new EglContext(this, _egl, ctx, surf); rv.MakeCurrent(null); return rv; } @@ -132,12 +133,12 @@ namespace Avalonia.OpenGL throw new OpenGlException("eglMakeCurrent failed"); } - public IGlSurface CreateWindowSurface(IntPtr window) + public EglSurface CreateWindowSurface(IntPtr window) { var s = _egl.CreateWindowSurface(_display, _config, window, new[] {EGL_NONE, EGL_NONE}); if (s == IntPtr.Zero) throw new OpenGlException("eglCreateWindowSurface failed"); - return new EglSurface(this, s); + return new EglSurface(this, _egl, s); } public int SampleCount @@ -157,49 +158,5 @@ namespace Avalonia.OpenGL return rv; } } - - class EglSurface : SafeHandle, IGlSurface - { - private readonly EglDisplay _display; - - public EglSurface(EglDisplay display, IntPtr surface) : base(surface, true) - { - _display = display; - } - - protected override bool ReleaseHandle() - { - _display._egl.DestroySurface(_display._display, handle); - return true; - } - - public override bool IsInvalid => handle == IntPtr.Zero; - - public IGlDisplay Display => _display; - public void SwapBuffers() => _display._egl.SwapBuffers(_display._display, handle); - } - - class EglContext : IGlContext - { - private readonly EglDisplay _disp; - - public EglContext(EglDisplay display, IntPtr ctx, IntPtr offscreenSurface) - { - _disp = display; - Context = ctx; - OffscreenSurface = offscreenSurface; - } - - public IntPtr Context { get; } - public IntPtr OffscreenSurface { get; } - public IGlDisplay Display => _disp; - - public void MakeCurrent(IGlSurface surface) - { - var surf = ((EglSurface)surface)?.DangerousGetHandle() ?? OffscreenSurface; - if (!_disp._egl.MakeCurrent(_disp._display, surf, surf, Context)) - throw new OpenGlException("eglMakeCurrent failed"); - } - } } } diff --git a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs index 535e32924e..86411b89da 100644 --- a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs +++ b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs @@ -7,7 +7,7 @@ namespace Avalonia.OpenGL { public IGlDisplay Display { get; set; } public IGlContext ImmediateContext { get; set; } - public IGlContext DeferredContext { get; set; } + public EglContext DeferredContext { get; set; } public static void TryInitialize() { @@ -26,7 +26,7 @@ namespace Avalonia.OpenGL { Display = disp, ImmediateContext = ctx, - DeferredContext = disp.CreateContext(ctx) + DeferredContext = (EglContext)disp.CreateContext(ctx) }; } catch(Exception e) diff --git a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs index c99c869c4f..f5dd413b0f 100644 --- a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs +++ b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; namespace Avalonia.OpenGL { @@ -12,10 +13,10 @@ namespace Avalonia.OpenGL } private readonly EglDisplay _display; - private readonly IGlContext _context; + private readonly EglContext _context; private readonly IEglWindowGlPlatformSurfaceInfo _info; - public EglGlPlatformSurface(EglDisplay display, IGlContext context, IEglWindowGlPlatformSurfaceInfo info) + public EglGlPlatformSurface(EglDisplay display, EglContext context, IEglWindowGlPlatformSurfaceInfo info) { _display = display; _context = context; @@ -30,11 +31,11 @@ namespace Avalonia.OpenGL class RenderTarget : IGlPlatformSurfaceRenderTarget { - private readonly IGlContext _context; - private readonly IGlSurface _glSurface; + private readonly EglContext _context; + private readonly EglSurface _glSurface; private readonly IEglWindowGlPlatformSurfaceInfo _info; - public RenderTarget(IGlContext context, IGlSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) + public RenderTarget(EglContext context, EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) { _context = context; _glSurface = glSurface; @@ -45,21 +46,33 @@ namespace Avalonia.OpenGL public IGlPlatformSurfaceRenderingSession BeginDraw() { - _context.MakeCurrent(_glSurface); - return new Session(_context, _glSurface, _info); + var l = _context.Lock(); + try + { + _context.MakeCurrent(_glSurface); + return new Session(_context, _glSurface, _info, l); + } + catch + { + l.Dispose(); + throw; + } } class Session : IGlPlatformSurfaceRenderingSession { private readonly IGlContext _context; - private readonly IGlSurface _glSurface; + private readonly EglSurface _glSurface; private readonly IEglWindowGlPlatformSurfaceInfo _info; + private IDisposable _lock; - public Session(IGlContext context, IGlSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) + public Session(IGlContext context, EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info, + IDisposable @lock) { _context = context; _glSurface = glSurface; _info = info; + _lock = @lock; } public void Dispose() @@ -67,6 +80,7 @@ namespace Avalonia.OpenGL _context.Display.GlInterface.Flush(); _glSurface.SwapBuffers(); _context.Display.ClearContext(); + _lock.Dispose(); } public IGlDisplay Display => _context.Display; diff --git a/src/Avalonia.OpenGL/EglSurface.cs b/src/Avalonia.OpenGL/EglSurface.cs new file mode 100644 index 0000000000..90bb1e36d0 --- /dev/null +++ b/src/Avalonia.OpenGL/EglSurface.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.OpenGL +{ + public class EglSurface : SafeHandle + { + private readonly EglDisplay _display; + private readonly EglInterface _egl; + + public EglSurface(EglDisplay display, EglInterface egl, IntPtr surface) : base(surface, true) + { + _display = display; + _egl = egl; + } + + protected override bool ReleaseHandle() + { + _egl.DestroySurface(_display.Handle, handle); + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + + public IGlDisplay Display => _display; + public void SwapBuffers() => _egl.SwapBuffers(_display.Handle, handle); + } +} \ No newline at end of file diff --git a/src/Avalonia.OpenGL/IGlContext.cs b/src/Avalonia.OpenGL/IGlContext.cs index 954567fe2e..04aa1b98e4 100644 --- a/src/Avalonia.OpenGL/IGlContext.cs +++ b/src/Avalonia.OpenGL/IGlContext.cs @@ -3,6 +3,6 @@ namespace Avalonia.OpenGL public interface IGlContext { IGlDisplay Display { get; } - void MakeCurrent(IGlSurface surface); + void MakeCurrent(); } -} \ No newline at end of file +} diff --git a/src/Avalonia.OpenGL/IGlSurface.cs b/src/Avalonia.OpenGL/IGlSurface.cs deleted file mode 100644 index 499d0a3c90..0000000000 --- a/src/Avalonia.OpenGL/IGlSurface.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Avalonia.OpenGL -{ - public interface IGlSurface : IDisposable - { - IGlDisplay Display { get; } - void SwapBuffers(); - } -} \ No newline at end of file diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index dfd277ec86..9b10d74c64 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -28,7 +28,7 @@ namespace Avalonia.Skia var iface = display.Type == GlDisplayType.OpenGL2 ? GRGlInterface.AssembleGlInterface((_, proc) => display.GlInterface.GetProcAddress(proc)) : GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc)); - gl.ImmediateContext.MakeCurrent(null); + gl.ImmediateContext.MakeCurrent(); GrContext = GRContext.Create(GRBackend.OpenGL, iface); } } From ec628116f6e52909bcd3df0dcaeb485d3c1e7c4b Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 17 Nov 2018 14:20:48 +0300 Subject: [PATCH 2/3] Force RenderComposite on UI thread --- .../Rendering/DeferredRenderer.cs | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index 40be0055d3..a268eff78a 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -144,7 +144,7 @@ namespace Avalonia.Rendering var t = (IRenderLoopTask)this; if(t.NeedsUpdate) UpdateScene(); - t.Render(); + Render(true); } /// @@ -176,12 +176,7 @@ namespace Avalonia.Rendering void IRenderLoopTask.Update(TimeSpan time) => UpdateScene(); - void IRenderLoopTask.Render() - { - using (var l = _lock.TryLock()) - if (l != null) - Render(); - } + void IRenderLoopTask.Render() => Render(false); /// Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush) @@ -202,17 +197,19 @@ namespace Avalonia.Rendering internal void UnitTestUpdateScene() => UpdateScene(); - internal void UnitTestRender() => Render(_scene.Item); + internal void UnitTestRender() => Render(_scene.Item, false); - private void Render() + private void Render(bool forceComposite) { - using (var scene = _scene?.Clone()) - { - Render(scene?.Item); - } + using (var l = _lock.TryLock()) + if (l != null) + using (var scene = _scene?.Clone()) + { + Render(scene?.Item, forceComposite); + } } - private void Render(Scene scene) + private void Render(Scene scene, bool forceComposite) { bool renderOverlay = DrawDirtyRects || DrawFps; bool composite = false; @@ -256,7 +253,7 @@ namespace Avalonia.Rendering RenderOverlay(scene, context); RenderComposite(scene, context); } - else if (composite) + else if (composite || forceComposite) { context = context ?? RenderTarget.CreateDrawingContext(this); RenderComposite(scene, context); From 5d56163040c4cba10bd28b61cdce16a9e1904c10 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 17 Nov 2018 11:46:57 +0000 Subject: [PATCH 3/3] update replace script to include avalonia.native. --- scripts/ReplaceNugetCache.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/ReplaceNugetCache.sh b/scripts/ReplaceNugetCache.sh index d50f4152e8..4cc11edd60 100755 --- a/scripts/ReplaceNugetCache.sh +++ b/scripts/ReplaceNugetCache.sh @@ -4,5 +4,6 @@ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard2.0/ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard2.0/ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.skia/$1/lib/netstandard2.0/ + cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.native/$1/lib/netstandard2.0/