Browse Source

Merge pull request #2108 from AvaloniaUI/extra-gl-locks

Lock GL context before drawing.
danwalmsley 7 years ago
parent
commit
e526608c45

+ 1 - 0
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/
  
  

+ 1 - 3
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();
         }
     }

+ 44 - 0
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");
+        }
+    }
+}

+ 5 - 48
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");
-            }
-        }
     }
 }

+ 2 - 2
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)

+ 23 - 9
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;

+ 28 - 0
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);
+    }
+}

+ 2 - 2
src/Avalonia.OpenGL/IGlContext.cs

@@ -3,6 +3,6 @@ namespace Avalonia.OpenGL
     public interface IGlContext
     {
         IGlDisplay Display { get; }
-        void MakeCurrent(IGlSurface surface);
+        void MakeCurrent();
     }
-}
+}

+ 0 - 10
src/Avalonia.OpenGL/IGlSurface.cs

@@ -1,10 +0,0 @@
-using System;
-
-namespace Avalonia.OpenGL
-{
-    public interface IGlSurface : IDisposable
-    {
-        IGlDisplay Display { get; }
-        void SwapBuffers();
-    }
-}

+ 12 - 15
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);
         }
 
         /// <inheritdoc/>
@@ -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);
 
         /// <inheritdoc/>
         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);

+ 1 - 1
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);
             }
         }