Browse Source

Refactorings for opengl context and surface management

Nikita Tsukanov 5 years ago
parent
commit
b9985a8fa0
52 changed files with 786 additions and 486 deletions
  1. 1 0
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  2. 4 4
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  3. 28 13
      src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs
  4. 3 3
      src/Avalonia.Native/PopupImpl.cs
  5. 3 3
      src/Avalonia.Native/WindowImpl.cs
  6. 1 1
      src/Avalonia.Native/WindowImplBase.cs
  7. 1 0
      src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
  8. 5 5
      src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
  9. 109 83
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  10. 1 1
      src/Avalonia.OpenGL/Egl/EglConsts.cs
  11. 45 24
      src/Avalonia.OpenGL/Egl/EglContext.cs
  12. 23 30
      src/Avalonia.OpenGL/Egl/EglDisplay.cs
  13. 1 1
      src/Avalonia.OpenGL/Egl/EglErrors.cs
  14. 54 0
      src/Avalonia.OpenGL/Egl/EglGlPlatformSurface.cs
  15. 22 25
      src/Avalonia.OpenGL/Egl/EglGlPlatformSurfaceBase.cs
  16. 1 1
      src/Avalonia.OpenGL/Egl/EglInterface.cs
  17. 72 0
      src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs
  18. 7 4
      src/Avalonia.OpenGL/Egl/EglSurface.cs
  19. 0 43
      src/Avalonia.OpenGL/EglGlPlatformFeature.cs
  20. 0 51
      src/Avalonia.OpenGL/EglGlPlatformSurface.cs
  21. 13 0
      src/Avalonia.OpenGL/GlInterface.cs
  22. 2 0
      src/Avalonia.OpenGL/IGlContext.cs
  23. 1 1
      src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs
  24. 13 0
      src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs
  25. 0 8
      src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs
  26. 17 0
      src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs
  27. 0 13
      src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs
  28. 16 18
      src/Avalonia.OpenGL/Imaging/OpenGlBitmap.cs
  29. 1 0
      src/Avalonia.OpenGL/OpenGlException.cs
  30. 1 1
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurface.cs
  31. 1 1
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs
  32. 1 1
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderingSession.cs
  33. 41 12
      src/Avalonia.X11/Glx/GlxContext.cs
  34. 4 4
      src/Avalonia.X11/Glx/GlxDisplay.cs
  35. 10 15
      src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
  36. 8 5
      src/Avalonia.X11/Glx/GlxPlatformFeature.cs
  37. 3 2
      src/Avalonia.X11/X11Platform.cs
  38. 5 4
      src/Avalonia.X11/X11Window.cs
  39. 2 2
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  40. 11 7
      src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
  41. 9 0
      src/Linux/Avalonia.LinuxFramebuffer/Output/IGlOutputBackend.cs
  42. 1 1
      src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
  43. 1 0
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
  44. 6 3
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
  45. 207 0
      src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
  46. 0 81
      src/Skia/Avalonia.Skia/Gpu/OpenGlTextureBitmapImpl.cs
  47. 3 3
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  48. 5 4
      src/Windows/Avalonia.Win32/Win32GlManager.cs
  49. 4 2
      src/Windows/Avalonia.Win32/WindowImpl.cs
  50. 16 4
      src/iOS/Avalonia.iOS/EaglDisplay.cs
  51. 2 1
      src/iOS/Avalonia.iOS/EaglLayerSurface.cs
  52. 1 1
      src/iOS/Avalonia.iOS/Platform.cs

+ 1 - 0
samples/ControlCatalog/Pages/OpenGlPage.xaml.cs

@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.OpenGL;
+using Avalonia.OpenGL.Controls;
 using Avalonia.Platform.Interop;
 using Avalonia.Threading;
 using static Avalonia.OpenGL.GlConsts;

+ 4 - 4
src/Avalonia.Native/AvaloniaNativePlatform.cs

@@ -16,7 +16,7 @@ namespace Avalonia.Native
     {
         private readonly IAvaloniaNativeFactory _factory;
         private AvaloniaNativePlatformOptions _options;
-        private GlPlatformFeature _glFeature;
+        private AvaloniaNativePlatformOpenGlInterface _platformGl;
 
         [DllImport("libAvaloniaNative")]
         static extern IntPtr CreateAvaloniaNative();
@@ -116,8 +116,8 @@ namespace Avalonia.Native
             {
                 try
                 {
-                    AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>()
-                        .ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay()));
+                    AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>()
+                        .ToConstant(_platformGl = new AvaloniaNativePlatformOpenGlInterface(_factory.ObtainGlDisplay()));
                 }
                 catch (Exception)
                 {
@@ -128,7 +128,7 @@ namespace Avalonia.Native
 
         public IWindowImpl CreateWindow()
         {
-            return new WindowImpl(_factory, _options, _glFeature);
+            return new WindowImpl(_factory, _options, _platformGl);
         }
 
         public IWindowImpl CreateEmbeddableWindow()

+ 28 - 13
src/Avalonia.Native/GlPlatformFeature.cs → src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs

@@ -2,21 +2,20 @@
 using Avalonia.OpenGL;
 using Avalonia.Native.Interop;
 using System.Drawing;
+using Avalonia.OpenGL.Surfaces;
 using Avalonia.Threading;
 
 namespace Avalonia.Native
 {
-    class GlPlatformFeature : IWindowingPlatformGlFeature
+    class AvaloniaNativePlatformOpenGlInterface : IPlatformOpenGlInterface
     {
         private readonly IAvnGlDisplay _display;
 
-        public GlPlatformFeature(IAvnGlDisplay display)
+        public AvaloniaNativePlatformOpenGlInterface(IAvnGlDisplay display)
         {
             _display = display;
             var immediate = display.CreateContext(null);
-            var deferred = display.CreateContext(immediate);
             
-
             int major, minor;
             GlInterface glInterface;
             using (immediate.MakeCurrent())
@@ -33,19 +32,22 @@ namespace Avalonia.Native
             }
 
             GlDisplay = new GlDisplay(display, glInterface, immediate.SampleCount, immediate.StencilSize);
-            
-            ImmediateContext = new GlContext(GlDisplay, immediate, _version);
-            DeferredContext = new GlContext(GlDisplay, deferred, _version);
+            MainContext = new GlContext(GlDisplay, null, immediate, _version);
         }
 
-        internal IGlContext ImmediateContext { get; }
-        public IGlContext MainContext => DeferredContext;
-        internal GlContext DeferredContext { get; }
+        internal GlContext MainContext { get; }
+        public IGlContext PrimaryContext => MainContext;
+        
+        public bool CanShareContexts => true;
+        public bool CanCreateContexts => true;
         internal GlDisplay GlDisplay;
         private readonly GlVersion _version;
 
+        public IGlContext CreateSharedContext() => new GlContext(GlDisplay,
+            MainContext, _display.CreateContext(MainContext.Context), _version);
+
         public IGlContext CreateContext() => new GlContext(GlDisplay,
-            _display.CreateContext(((GlContext)ImmediateContext).Context), _version);
+            null, _display.CreateContext(null), _version);
     }
 
     class GlDisplay
@@ -72,11 +74,13 @@ namespace Avalonia.Native
     class GlContext : IGlContext
     {
         private readonly GlDisplay _display;
+        private readonly GlContext _sharedWith;
         public IAvnGlContext Context { get; private set; }
 
-        public GlContext(GlDisplay display, IAvnGlContext context, GlVersion version)
+        public GlContext(GlDisplay display, GlContext sharedWith, IAvnGlContext context, GlVersion version)
         {
             _display = display;
+            _sharedWith = sharedWith;
             Context = context;
             Version = version;
         }
@@ -86,6 +90,17 @@ namespace Avalonia.Native
         public int SampleCount => _display.SampleCount;
         public int StencilSize => _display.StencilSize;
         public IDisposable MakeCurrent() => Context.MakeCurrent();
+        public IDisposable EnsureCurrent() => MakeCurrent();
+
+        public bool IsSharedWith(IGlContext context)
+        {
+            var c = (GlContext)context;
+            return c == this
+                   || c._sharedWith == this
+                   || _sharedWith == context
+                   || _sharedWith != null && _sharedWith == c._sharedWith;
+        }
+
 
         public void Dispose()
         {
@@ -108,7 +123,7 @@ namespace Avalonia.Native
 
         public IGlPlatformSurfaceRenderingSession BeginDraw()
         {
-            var feature = (GlPlatformFeature)AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
+            var feature = (AvaloniaNativePlatformOpenGlInterface)AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
             return new GlPlatformSurfaceRenderingSession(_context, _target.BeginDrawing());
         }
 

+ 3 - 3
src/Avalonia.Native/PopupImpl.cs

@@ -9,12 +9,12 @@ namespace Avalonia.Native
     {
         private readonly IAvaloniaNativeFactory _factory;
         private readonly AvaloniaNativePlatformOptions _opts;
-        private readonly GlPlatformFeature _glFeature;
+        private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
         private readonly IWindowBaseImpl _parent;
 
         public PopupImpl(IAvaloniaNativeFactory factory,
             AvaloniaNativePlatformOptions opts,
-            GlPlatformFeature glFeature,
+            AvaloniaNativePlatformOpenGlInterface glFeature,
             IWindowBaseImpl parent) : base(opts, glFeature)
         {
             _factory = factory;
@@ -23,7 +23,7 @@ namespace Avalonia.Native
             _parent = parent;
             using (var e = new PopupEvents(this))
             {
-                var context = _opts.UseGpu ? glFeature?.DeferredContext : null;
+                var context = _opts.UseGpu ? glFeature?.MainContext : null;
                 Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context);
             }
             PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));

+ 3 - 3
src/Avalonia.Native/WindowImpl.cs

@@ -14,19 +14,19 @@ namespace Avalonia.Native
     {
         private readonly IAvaloniaNativeFactory _factory;
         private readonly AvaloniaNativePlatformOptions _opts;
-        private readonly GlPlatformFeature _glFeature;
+        private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
         IAvnWindow _native;
         private double _extendTitleBarHeight = -1;
 
         internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
-            GlPlatformFeature glFeature) : base(opts, glFeature)
+            AvaloniaNativePlatformOpenGlInterface glFeature) : base(opts, glFeature)
         {
             _factory = factory;
             _opts = opts;
             _glFeature = glFeature;
             using (var e = new WindowEvents(this))
             {
-                var context = _opts.UseGpu ? glFeature?.DeferredContext : null;
+                var context = _opts.UseGpu ? glFeature?.MainContext : null;
                 Init(_native = factory.CreateWindow(e, context?.Context), factory.CreateScreens(), context);
             }
 

+ 1 - 1
src/Avalonia.Native/WindowImplBase.cs

@@ -61,7 +61,7 @@ namespace Avalonia.Native
         private NativeControlHostImpl _nativeControlHost;
         private IGlContext _glContext;
 
-        internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, GlPlatformFeature glFeature)
+        internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, AvaloniaNativePlatformOpenGlInterface glFeature)
         {
             _gpu = opts.UseGpu && glFeature != null;
             _deferredRendering = opts.UseDeferredRendering;

+ 1 - 0
src/Avalonia.OpenGL/Angle/AngleEglInterface.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Runtime.InteropServices;
+using Avalonia.OpenGL.Egl;
 using Avalonia.Platform;
 using Avalonia.Platform.Interop;
 

+ 5 - 5
src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs

@@ -1,8 +1,8 @@
 using System;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
-
-using static Avalonia.OpenGL.EglConsts;
+using Avalonia.OpenGL.Egl;
+using static Avalonia.OpenGL.Egl.EglConsts;
 
 namespace Avalonia.OpenGL.Angle
 {
@@ -52,7 +52,7 @@ namespace Avalonia.OpenGL.Angle
             }
         }
 
-        private AngleWin32EglDisplay(EglInterface egl, AngleInfo info) : base(egl, info.Display)
+        private AngleWin32EglDisplay(EglInterface egl, AngleInfo info) : base(egl, false, info.Display)
         {
             PlatformApi = info.PlatformApi;
         }
@@ -78,11 +78,11 @@ namespace Avalonia.OpenGL.Angle
             return d3dDeviceHandle;
         }
 
-        public EglSurface WrapDirect3D11Texture(IntPtr handle)
+        public EglSurface WrapDirect3D11Texture(EglPlatformOpenGlInterface egl, IntPtr handle)
         {
             if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
                 throw new InvalidOperationException("Current platform API is " + PlatformApi);
-            return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
+            return  egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
         }
     }
 }

+ 109 - 83
src/Avalonia.OpenGL/OpenGlControlBase.cs → src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs

@@ -3,44 +3,83 @@ using Avalonia.Controls;
 using Avalonia.Logging;
 using Avalonia.Media;
 using Avalonia.OpenGL.Imaging;
-using Avalonia.Rendering;
-using Avalonia.VisualTree;
 using static Avalonia.OpenGL.GlConsts;
 
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Controls
 {
     public abstract class OpenGlControlBase : Control
     {
         private IGlContext _context;
-        private int _fb, _texture, _renderBuffer;
-        private OpenGlTextureBitmap _bitmap;
-        private PixelSize _oldSize;
+        private int _fb, _depthBuffer;
+        private OpenGlBitmap _bitmap;
+        private IOpenGlBitmapAttachment _attachment;
+        private PixelSize _depthBufferSize;
         private bool _glFailed;
+        private bool _initialized;
         protected GlVersion GlVersion { get; private set; }
         public sealed override void Render(DrawingContext context)
         {
             if(!EnsureInitialized())
                 return;
-                
+            
             using (_context.MakeCurrent())
             {
-                using (_bitmap.Lock())
-                {
-                    var gl = _context.GlInterface;
-                    gl.BindFramebuffer(GL_FRAMEBUFFER, _fb);
-                    if (_oldSize != GetPixelSize())
-                        ResizeTexture(gl);
-
-                    OnOpenGlRender(gl, _fb);
-                    gl.Flush();
-                }
+                _context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, _fb);
+                EnsureTextureAttachment();
+                EnsureDepthBufferAttachment(_context.GlInterface);
+                if(!CheckFramebufferStatus(_context.GlInterface))
+                    return;
+                
+                OnOpenGlRender(_context.GlInterface, _fb);
+                _attachment.Present();
             }
 
             context.DrawImage(_bitmap, new Rect(_bitmap.Size), Bounds);
             base.Render(context);
         }
+        
+        private void CheckError(GlInterface gl)
+        {
+            int err;
+            while ((err = gl.GetError()) != GL_NO_ERROR)
+                Console.WriteLine(err);
+        }
+
+        void EnsureTextureAttachment()
+        {
+            _context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, _fb);
+            if (_bitmap == null || _attachment == null || _bitmap.PixelSize != GetPixelSize())
+            {
+                _attachment?.Dispose();
+                _attachment = null;
+                _bitmap?.Dispose();
+                _bitmap = null;
+                _bitmap = new OpenGlBitmap(GetPixelSize(), new Vector(96, 96));
+                _attachment = _bitmap.CreateFramebufferAttachment(_context);
+            }
+        }
+        
+        void EnsureDepthBufferAttachment(GlInterface gl)
+        {
+            var size = GetPixelSize();
+            if (size == _depthBufferSize && _depthBuffer != 0)
+                return;
+                    
+            gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderBuffer);
+            if (_depthBuffer != 0) gl.DeleteRenderbuffers(1, new[] { _depthBuffer });
+
+            var oneArr = new int[1];
+            gl.GenRenderbuffers(1, oneArr);
+            _depthBuffer = oneArr[0];
+            gl.BindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
+            gl.RenderbufferStorage(GL_RENDERBUFFER,
+                GlVersion.Type == GlProfileType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT,
+                size.Width, size.Height);
+            gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
+            gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer);
+        }
 
-        void DoCleanup(bool callUserDeinit)
+        void DoCleanup()
         {
             if (_context != null)
             {
@@ -50,16 +89,19 @@ namespace Avalonia.OpenGL
                     gl.BindTexture(GL_TEXTURE_2D, 0);
                     gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
                     gl.DeleteFramebuffers(1, new[] { _fb });
-                    using (_bitmap.Lock()) 
-                        _bitmap.SetTexture(0, 0, new PixelSize(1, 1), 1);
-                    gl.DeleteTextures(1, new[] { _texture });
-                    gl.DeleteRenderbuffers(1, new[] { _renderBuffer });
-                    _bitmap.Dispose();
+                    gl.DeleteRenderbuffers(1, new[] { _depthBuffer });
+                    _attachment?.Dispose();
+                    _attachment = null;
+                    _bitmap?.Dispose();
+                    _bitmap = null;
                     
                     try
                     {
-                        if (callUserDeinit)
+                        if (_initialized)
+                        {
+                            _initialized = false;
                             OnOpenGlDeinit(_context.GlInterface, _fb);
+                        }
                     }
                     finally
                     {
@@ -72,11 +114,11 @@ namespace Avalonia.OpenGL
 
         protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
         {
-            DoCleanup(true);
+            DoCleanup();
             base.OnDetachedFromVisualTree(e);
         }
 
-        bool EnsureInitialized()
+        private bool EnsureInitializedCore()
         {
             if (_context != null)
                 return true;
@@ -84,34 +126,43 @@ namespace Avalonia.OpenGL
             if (_glFailed)
                 return false;
             
-            var feature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
+            var feature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
             if (feature == null)
                 return false;
+            if (!feature.CanShareContexts)
+            {
+                Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
+                    "Unable to initialize OpenGL: current platform does not support multithreaded context sharing");
+                return false;
+            }
             try
             {
-                _context = feature.CreateContext();
-
+                _context = feature.CreateSharedContext();
             }
             catch (Exception e)
             {
                 Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
                     "Unable to initialize OpenGL: unable to create additional OpenGL context: {exception}", e);
-                _glFailed = true;
                 return false;
             }
 
             GlVersion = _context.Version;
             try
             {
-                _bitmap = new OpenGlTextureBitmap();
+                _bitmap = new OpenGlBitmap(GetPixelSize(), new Vector(96, 96));
+                if (!_bitmap.SupportsContext(_context))
+                {
+                    Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
+                        "Unable to initialize OpenGL: unable to create OpenGlBitmap: OpenGL context is not compatible");
+                    return false;
+                }
             }
             catch (Exception e)
             {
                 _context.Dispose();
                 _context = null;
                 Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
-                    "Unable to initialize OpenGL: unable to create OpenGlTextureBitmap: {exception}", e);
-                _glFailed = true;
+                    "Unable to initialize OpenGL: unable to create OpenGlBitmap: {exception}", e);
                 return false;
             }
 
@@ -119,80 +170,55 @@ namespace Avalonia.OpenGL
             {
                 try
                 {
-                    _oldSize = GetPixelSize();
+                    _depthBufferSize = GetPixelSize();
                     var gl = _context.GlInterface;
                     var oneArr = new int[1];
                     gl.GenFramebuffers(1, oneArr);
                     _fb = oneArr[0];
                     gl.BindFramebuffer(GL_FRAMEBUFFER, _fb);
-
-                    gl.GenTextures(1, oneArr);
-                    _texture = oneArr[0];
                     
-                    ResizeTexture(gl);
-
-                    gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
+                    EnsureDepthBufferAttachment(gl);
+                    EnsureTextureAttachment();
 
-                    var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
-                    if (status != GL_FRAMEBUFFER_COMPLETE)
-                    {
-                        int code;
-                        while ((code = gl.GetError()) != 0)
-                            Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
-                                "Unable to initialize OpenGL FBO: {code}", code);
-
-                        _glFailed = true;
-                        return false;
-                    }
+                    return CheckFramebufferStatus(gl);
                 }
                 catch(Exception e)
                 {
                     Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
                         "Unable to initialize OpenGL FBO: {exception}", e);
-                    _glFailed = true;
+                    return false;
                 }
-
-                if (!_glFailed)
-                    OnOpenGlInit(_context.GlInterface, _fb);
             }
+        }
 
-            if (_glFailed)
+        private bool CheckFramebufferStatus(GlInterface gl)
+        {
+            var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
+            if (status != GL_FRAMEBUFFER_COMPLETE)
             {
-                DoCleanup(false);
+                int code;
+                while ((code = gl.GetError()) != 0)
+                    Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
+                        "Unable to initialize OpenGL FBO: {code}", code);
+                return false;
             }
 
             return true;
         }
 
-        void ResizeTexture(GlInterface gl)
+        private bool EnsureInitialized()
         {
-            var size = GetPixelSize();
-
-            gl.GetIntegerv( GL_TEXTURE_BINDING_2D, out var oldTexture);
-            gl.BindTexture(GL_TEXTURE_2D, _texture);
-            gl.TexImage2D(GL_TEXTURE_2D, 0, 
-                GlVersion.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8,
-                size.Width, size.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero);
-            gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-            gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-            gl.BindTexture(GL_TEXTURE_2D, oldTexture);
-
-            gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderBuffer);
-            gl.DeleteRenderbuffers(1, new[] { _renderBuffer });
-            var oneArr = new int[1];
-            gl.GenRenderbuffers(1, oneArr);
-            _renderBuffer = oneArr[0];
-            gl.BindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
-            gl.RenderbufferStorage(GL_RENDERBUFFER,
-                GlVersion.Type == GlProfileType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT,
-                size.Width, size.Height);
-            gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffer);
-            gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer);
-            using (_bitmap.Lock())
-                _bitmap.SetTexture(_texture, GL_RGBA8, size, 1);
+            if (_initialized)
+                return true;
+            _glFailed = !(_initialized = EnsureInitializedCore());
+            if (_glFailed)
+                return false;
+            using (_context.MakeCurrent())
+                OnOpenGlInit(_context.GlInterface, _fb);
+            return true;
         }
         
-        PixelSize GetPixelSize()
+        private PixelSize GetPixelSize()
         {
             var scaling = VisualRoot.RenderScaling;
             return new PixelSize(Math.Max(1, (int)(Bounds.Width * scaling)),

+ 1 - 1
src/Avalonia.OpenGL/EglConsts.cs → src/Avalonia.OpenGL/Egl/EglConsts.cs

@@ -1,6 +1,6 @@
 // ReSharper disable UnusedMember.Global
 // ReSharper disable IdentifierTypo
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Egl
 {
     public static class EglConsts
     {

+ 45 - 24
src/Avalonia.OpenGL/EglContext.cs → src/Avalonia.OpenGL/Egl/EglContext.cs

@@ -1,23 +1,25 @@
 using System;
 using System.Reactive.Disposables;
 using System.Threading;
-using static Avalonia.OpenGL.EglConsts;
+using static Avalonia.OpenGL.Egl.EglConsts;
 
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Egl
 {
     public class EglContext : IGlContext
     {
         private readonly EglDisplay _disp;
         private readonly EglInterface _egl;
+        private readonly EglContext _sharedWith;
         private readonly object _lock = new object();
 
-        public EglContext(EglDisplay display, EglInterface egl, IntPtr ctx, EglSurface offscreenSurface,
+        public EglContext(EglDisplay display, EglInterface egl, EglContext sharedWith, IntPtr ctx, Func<EglContext, EglSurface> offscreenSurface,
             GlVersion version, int sampleCount, int stencilSize)
         {
             _disp = display;
             _egl = egl;
+            _sharedWith = sharedWith;
             Context = ctx;
-            OffscreenSurface = offscreenSurface;
+            OffscreenSurface = offscreenSurface(this);
             Version = version;
             SampleCount = sampleCount;
             StencilSize = stencilSize;
@@ -33,21 +35,17 @@ namespace Avalonia.OpenGL
         public int StencilSize { get; }
         public EglDisplay Display => _disp;
 
-        public IDisposable Lock()
-        {
-            Monitor.Enter(_lock);
-            return Disposable.Create(() => Monitor.Exit(_lock));
-        }
-
         class RestoreContext : IDisposable
         {
             private readonly EglInterface _egl;
+            private readonly object _l;
             private readonly IntPtr _display;
             private IntPtr _context, _read, _draw;
 
-            public RestoreContext(EglInterface egl, IntPtr defDisplay)
+            public RestoreContext(EglInterface egl, IntPtr defDisplay, object l)
             {
                 _egl = egl;
+                _l = l;
                 _display = _egl.GetCurrentDisplay();
                 if (_display == IntPtr.Zero)
                     _display = defDisplay;
@@ -59,29 +57,52 @@ namespace Avalonia.OpenGL
             public void Dispose() 
             {
                 _egl.MakeCurrent(_display, _draw, _read, _context);
+                Monitor.Exit(_l);
             }
 
         }
 
-        public IDisposable MakeCurrent()
+        public IDisposable MakeCurrent() => MakeCurrent(OffscreenSurface);
+
+        public IDisposable MakeCurrent(EglSurface surface)
         {
-            var old = new RestoreContext(_egl, _disp.Handle);
-            _egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
-            if (!_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, Context))
-                throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
-            return old;
+            Monitor.Enter(_lock);
+            var success = false;
+            try
+            {
+                var old = new RestoreContext(_egl, _disp.Handle, _lock);
+                var surf = surface ?? OffscreenSurface;
+                _egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+                if (!_egl.MakeCurrent(_disp.Handle, surf.DangerousGetHandle(), surf.DangerousGetHandle(), Context))
+                    throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
+                success = true;
+                return old;
+            }
+            finally
+            {
+                if(!success)
+                    Monitor.Enter(_lock);
+            }
         }
         
-        public IDisposable MakeCurrent(EglSurface surface)
+        public IDisposable EnsureCurrent()
         {
-            var old = new RestoreContext(_egl, _disp.Handle);
-            var surf = surface ?? OffscreenSurface;
-            _egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
-            if (!_egl.MakeCurrent(_disp.Handle, surf.DangerousGetHandle(), surf.DangerousGetHandle(), Context))
-                throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
-            return old;
+            if(IsCurrent)
+                return Disposable.Empty;
+            return MakeCurrent();
         }
 
+        public bool IsSharedWith(IGlContext context)
+        {
+            var c = (EglContext)context;
+            return c == this
+                   || c._sharedWith == this
+                   || _sharedWith == context
+                   || _sharedWith != null && _sharedWith == c._sharedWith;
+        }
+
+        public bool IsCurrent => _egl.GetCurrentDisplay() == _disp.Handle && _egl.GetCurrentContext() == Context;
+
         public void Dispose()
         {
             _egl.DestroyContext(_disp.Handle, Context);

+ 23 - 30
src/Avalonia.OpenGL/EglDisplay.cs → src/Avalonia.OpenGL/Egl/EglDisplay.cs

@@ -1,26 +1,25 @@
 using System;
-using System.Collections.Generic;
 using System.Linq;
-using System.Runtime.InteropServices;
-using Avalonia.Platform.Interop;
-using static Avalonia.OpenGL.EglConsts;
+using static Avalonia.OpenGL.Egl.EglConsts;
 
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Egl
 {
     public class EglDisplay
     {
         private readonly EglInterface _egl;
+        public bool SupportsSharing { get; }
         private readonly IntPtr _display;
         private readonly IntPtr _config;
         private readonly int[] _contextAttributes;
         private readonly int _surfaceType;
 
         public IntPtr Handle => _display;
+        public IntPtr Config => _config;
         private int _sampleCount;
         private int _stencilSize;
         private GlVersion _version;
 
-        public EglDisplay(EglInterface egl) : this(egl, -1, IntPtr.Zero, null)
+        public EglDisplay(EglInterface egl, bool supportsSharing) : this(egl, supportsSharing, -1, IntPtr.Zero, null)
         {
             
         }
@@ -45,15 +44,16 @@ namespace Avalonia.OpenGL
             return display;
         }
 
-        public EglDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs)
-            : this(egl, CreateDisplay(egl, platformType, platformDisplay, attrs))
+        public EglDisplay(EglInterface egl, bool supportsSharing, int platformType, IntPtr platformDisplay, int[] attrs)
+            : this(egl, supportsSharing, CreateDisplay(egl, platformType, platformDisplay, attrs))
         {
 
         }
 
-        public EglDisplay(EglInterface egl, IntPtr display)
+        public EglDisplay(EglInterface egl, bool supportsSharing, IntPtr display)
         {
             _egl = egl;
+            SupportsSharing = supportsSharing;
             _display = display;
             if(_display == IntPtr.Zero)
                 throw new ArgumentException();
@@ -136,7 +136,12 @@ namespace Avalonia.OpenGL
                 throw new OpenGlException("No suitable EGL config was found");
         }
 
-        public EglDisplay() : this(new EglInterface())
+        public EglDisplay() : this(false)
+        {
+            
+        }
+        
+        public EglDisplay(bool supportsSharing) : this(new EglInterface(), supportsSharing)
         {
             
         }
@@ -144,6 +149,9 @@ namespace Avalonia.OpenGL
         public EglInterface EglInterface => _egl;
         public EglContext CreateContext(IGlContext share)
         {
+            if (share != null && !SupportsSharing)
+                throw new NotSupportedException("Context sharing is not supported by this display");
+            
             if((_surfaceType|EGL_PBUFFER_BIT) == 0)
                 throw new InvalidOperationException("Platform doesn't support PBUFFER surfaces");
             var shareCtx = (EglContext)share;
@@ -158,37 +166,22 @@ namespace Avalonia.OpenGL
             });
             if (surf == IntPtr.Zero)
                 throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl);
-            var rv = new EglContext(this, _egl, ctx, new EglSurface(this, _egl, surf),
+            var rv = new EglContext(this, _egl, shareCtx, ctx, context => new EglSurface(this, context, surf),
                 _version, _sampleCount, _stencilSize);
             return rv;
         }
 
         public EglContext CreateContext(EglContext share, EglSurface offscreenSurface)
         {
+            if (share != null && !SupportsSharing)
+                throw new NotSupportedException("Context sharing is not supported by this display");
+
             var ctx = _egl.CreateContext(_display, _config, share?.Context ?? IntPtr.Zero, _contextAttributes);
             if (ctx == IntPtr.Zero)
                 throw OpenGlException.GetFormattedException("eglCreateContext", _egl);
-            var rv = new EglContext(this, _egl, ctx, offscreenSurface, _version, _sampleCount, _stencilSize);
+            var rv = new EglContext(this, _egl, share, ctx, _ => offscreenSurface, _version, _sampleCount, _stencilSize);
             rv.MakeCurrent(null);
             return rv;
         }
-
-        public EglSurface CreateWindowSurface(IntPtr window)
-        {
-            var s = _egl.CreateWindowSurface(_display, _config, window, new[] {EGL_NONE, EGL_NONE});
-            if (s == IntPtr.Zero)
-                throw OpenGlException.GetFormattedException("eglCreateWindowSurface", _egl);
-            return new EglSurface(this, _egl, s);
-        }
-        
-        public EglSurface CreatePBufferFromClientBuffer (int bufferType, IntPtr handle, int[] attribs)
-        {
-            var s = _egl.CreatePbufferFromClientBuffer(_display, bufferType, handle,
-                _config, attribs);         
-
-            if (s == IntPtr.Zero)
-                throw OpenGlException.GetFormattedException("eglCreatePbufferFromClientBuffer", _egl);
-            return new EglSurface(this, _egl, s);
-        }
     }
 }

+ 1 - 1
src/Avalonia.OpenGL/EglErrors.cs → src/Avalonia.OpenGL/Egl/EglErrors.cs

@@ -1,4 +1,4 @@
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Egl
 {
     public enum EglErrors
     {

+ 54 - 0
src/Avalonia.OpenGL/Egl/EglGlPlatformSurface.cs

@@ -0,0 +1,54 @@
+using Avalonia.OpenGL.Surfaces;
+
+namespace Avalonia.OpenGL.Egl
+{
+    public class EglGlPlatformSurface : EglGlPlatformSurfaceBase
+    {
+        private readonly EglPlatformOpenGlInterface _egl;
+        private readonly IEglWindowGlPlatformSurfaceInfo _info;
+        
+        public EglGlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) : base()
+        {
+            _egl = egl;
+            _info = info;
+        }
+
+        public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+        {
+            var glSurface = _egl.CreateWindowSurface(_info.Handle);
+            return new RenderTarget(_egl, glSurface, _info);
+        }
+
+        class RenderTarget : EglPlatformSurfaceRenderTargetBase
+        {
+            private readonly EglPlatformOpenGlInterface _egl;
+            private EglSurface _glSurface;
+            private readonly IEglWindowGlPlatformSurfaceInfo _info;
+            private PixelSize _currentSize;
+
+            public RenderTarget(EglPlatformOpenGlInterface egl,
+                EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(egl)
+            {
+                _egl = egl;
+                _glSurface = glSurface;
+                _info = info;
+                _currentSize = info.Size;
+            }
+
+            public override void Dispose() => _glSurface.Dispose();
+            
+            public override IGlPlatformSurfaceRenderingSession BeginDraw()
+            {
+                if (_info.Size != _currentSize || _glSurface == null)
+                {
+                    _glSurface?.Dispose();
+                    _glSurface = null;
+                    _glSurface = _egl.CreateWindowSurface(_info.Handle);
+                    _currentSize = _info.Size;
+                }
+                return base.BeginDraw(_glSurface, _info);
+            }
+        }
+    }
+}
+

+ 22 - 25
src/Avalonia.OpenGL/EglGlPlatformSurfaceBase.cs → src/Avalonia.OpenGL/Egl/EglGlPlatformSurfaceBase.cs

@@ -1,6 +1,7 @@
 using System;
+using Avalonia.OpenGL.Surfaces;
 
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Egl
 {
     public abstract class EglGlPlatformSurfaceBase : IGlPlatformSurface
     {
@@ -14,19 +15,15 @@ namespace Avalonia.OpenGL
         public abstract IGlPlatformSurfaceRenderTarget CreateGlRenderTarget();
     }
 
-    public abstract class EglPlatformSurfaceRenderTargetBase  : IGlPlatformSurfaceRenderTargetWithCorruptionInfo
+    public abstract class EglPlatformSurfaceRenderTargetBase  : IGlPlatformSurfaceRenderTarget
     {
-        private readonly EglDisplay _display;
-        private readonly EglContext _context;
+        private readonly EglPlatformOpenGlInterface _egl;
 
-        protected EglPlatformSurfaceRenderTargetBase(EglDisplay display, EglContext context)
+        protected EglPlatformSurfaceRenderTargetBase(EglPlatformOpenGlInterface egl)
         {
-            _display = display;
-            _context = context;
+            _egl = egl;
         }
 
-        public abstract bool IsCorrupted { get; }
-
         public virtual void Dispose()
         {
             
@@ -37,22 +34,25 @@ namespace Avalonia.OpenGL
         protected IGlPlatformSurfaceRenderingSession BeginDraw(EglSurface surface,
             EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, Action onFinish = null, bool isYFlipped = false)
         {
-            var l = _context.Lock();
+
+            var restoreContext = _egl.PrimaryEglContext.MakeCurrent(surface);
+            var success = false;
             try
             {
-                if (IsCorrupted)
-                    throw new RenderTargetCorruptedException();
-                var restoreContext = _context.MakeCurrent(surface);
-                _display.EglInterface.WaitClient();
-                _display.EglInterface.WaitGL();
-                _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
-                    
-                return new Session(_display, _context, surface, info, l, restoreContext, onFinish, isYFlipped);
+                var egli = _egl.Display.EglInterface;
+                egli.WaitClient();
+                egli.WaitGL();
+                egli.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
+                
+                _egl.PrimaryContext.GlInterface.BindFramebuffer(GlConsts.GL_FRAMEBUFFER, 0);
+                
+                success = true;
+                return new Session(_egl.Display, _egl.PrimaryEglContext, surface, info,  restoreContext, onFinish, isYFlipped);
             }
-            catch
+            finally
             {
-                l.Dispose();
-                throw;
+                if(!success)
+                    restoreContext.Dispose();
             }
         }
         
@@ -62,21 +62,19 @@ namespace Avalonia.OpenGL
             private readonly EglSurface _glSurface;
             private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info;
             private readonly EglDisplay _display;
-            private readonly IDisposable _lock;
             private readonly IDisposable _restoreContext;
             private readonly Action _onFinish;
 
 
             public Session(EglDisplay display, EglContext context,
                 EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info,
-                IDisposable @lock, IDisposable restoreContext, Action onFinish, bool isYFlipped)
+                 IDisposable restoreContext, Action onFinish, bool isYFlipped)
             {
                 IsYFlipped = isYFlipped;
                 _context = context;
                 _display = display;
                 _glSurface = glSurface;
                 _info = info;
-                _lock = @lock;
                 _restoreContext = restoreContext;
                 _onFinish = onFinish;
             }
@@ -90,7 +88,6 @@ namespace Avalonia.OpenGL
                 _display.EglInterface.WaitGL();
                 _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
                 _restoreContext.Dispose();
-                _lock.Dispose();
                 _onFinish?.Invoke();
             }
 

+ 1 - 1
src/Avalonia.OpenGL/EglInterface.cs → src/Avalonia.OpenGL/Egl/EglInterface.cs

@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
 using Avalonia.Platform;
 using Avalonia.Platform.Interop;
 
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Egl
 {
     public class EglInterface : GlInterfaceBase
     {

+ 72 - 0
src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs

@@ -0,0 +1,72 @@
+using System;
+using Avalonia.Logging;
+using static Avalonia.OpenGL.Egl.EglConsts;
+
+namespace Avalonia.OpenGL.Egl
+{
+    public class EglPlatformOpenGlInterface : IPlatformOpenGlInterface
+    {
+        public EglDisplay Display { get; private set; }
+        public bool CanCreateContexts => true;
+        public bool CanShareContexts => Display.SupportsSharing;
+        
+        public EglContext PrimaryEglContext { get; }
+        public IGlContext PrimaryContext => PrimaryEglContext;
+        
+        public EglPlatformOpenGlInterface(EglDisplay display)
+        {
+            Display = display;
+            PrimaryEglContext = display.CreateContext(null);
+        }
+        
+        public static void TryInitialize()
+        {
+            var feature = TryCreate();
+            if (feature != null)
+                AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(feature);
+        }
+        
+        public static EglPlatformOpenGlInterface TryCreate() => TryCreate(() => new EglDisplay());
+        public static EglPlatformOpenGlInterface TryCreate(Func<EglDisplay> displayFactory)
+        {
+            try
+            {
+                return new EglPlatformOpenGlInterface(displayFactory());
+            }
+            catch(Exception e)
+            {
+                Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(null, "Unable to initialize EGL-based rendering: {0}", e);
+                return null;
+            }
+        }
+
+        public IGlContext CreateContext() => Display.CreateContext(null);
+        public IGlContext CreateSharedContext() => Display.CreateContext(PrimaryEglContext);
+        
+
+        public EglSurface CreateWindowSurface(IntPtr window)
+        {
+            using (PrimaryContext.MakeCurrent())
+            {
+                var s = Display.EglInterface.CreateWindowSurface(Display.Handle, Display.Config, window,
+                    new[] { EGL_NONE, EGL_NONE });
+                if (s == IntPtr.Zero)
+                    throw OpenGlException.GetFormattedException("eglCreateWindowSurface", Display.EglInterface);
+                return new EglSurface(Display, PrimaryEglContext, s);
+            }
+        }
+        
+        public EglSurface CreatePBufferFromClientBuffer (int bufferType, IntPtr handle, int[] attribs)
+        {
+            using (PrimaryContext.MakeCurrent())
+            {
+                var s = Display.EglInterface.CreatePbufferFromClientBuffer(Display.Handle, bufferType, handle,
+                    Display.Config, attribs);
+
+                if (s == IntPtr.Zero)
+                    throw OpenGlException.GetFormattedException("eglCreatePbufferFromClientBuffer", Display.EglInterface);
+                return new EglSurface(Display, PrimaryEglContext, s);
+            }
+        }
+    }
+}

+ 7 - 4
src/Avalonia.OpenGL/EglSurface.cs → src/Avalonia.OpenGL/Egl/EglSurface.cs

@@ -1,22 +1,25 @@
 using System;
 using System.Runtime.InteropServices;
 
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Egl
 {
     public class EglSurface : SafeHandle
     {
         private readonly EglDisplay _display;
+        private readonly EglContext _context;
         private readonly EglInterface _egl;
 
-        public EglSurface(EglDisplay display, EglInterface egl, IntPtr surface)  : base(surface, true)
+        public EglSurface(EglDisplay display, EglContext context, IntPtr surface)  : base(surface, true)
         {
             _display = display;
-            _egl = egl;
+            _context = context;
+            _egl = display.EglInterface;
         }
 
         protected override bool ReleaseHandle()
         {
-            _egl.DestroySurface(_display.Handle, handle);
+            using (_context.MakeCurrent())
+                _egl.DestroySurface(_display.Handle, handle);
             return true;
         }
 

+ 0 - 43
src/Avalonia.OpenGL/EglGlPlatformFeature.cs

@@ -1,43 +0,0 @@
-using System;
-using Avalonia.Logging;
-
-namespace Avalonia.OpenGL
-{
-    public class EglGlPlatformFeature : IWindowingPlatformGlFeature
-    {
-        private EglDisplay _display;
-        public EglDisplay Display => _display;
-        public IGlContext CreateContext()
-        {
-            return _display.CreateContext(DeferredContext);
-        }
-        public EglContext DeferredContext { get; private set; }
-        public IGlContext MainContext => DeferredContext;
-
-        public static void TryInitialize()
-        {
-            var feature = TryCreate();
-            if (feature != null)
-                AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature);
-        }
-
-        public static EglGlPlatformFeature TryCreate() => TryCreate(() => new EglDisplay());
-        public static EglGlPlatformFeature TryCreate(Func<EglDisplay> displayFactory)
-        {
-            try
-            {
-                var disp = displayFactory();
-                return new EglGlPlatformFeature
-                {
-                    _display = disp,
-                    DeferredContext = disp.CreateContext(null)
-                };
-            }
-            catch(Exception e)
-            {
-                Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(null, "Unable to initialize EGL-based rendering: {0}", e);
-                return null;
-            }
-        }
-    }
-}

+ 0 - 51
src/Avalonia.OpenGL/EglGlPlatformSurface.cs

@@ -1,51 +0,0 @@
-using System;
-using System.Threading;
-
-namespace Avalonia.OpenGL
-{
-    public class EglGlPlatformSurface : EglGlPlatformSurfaceBase
-    {
-        private readonly EglDisplay _display;
-        private readonly EglContext _context;
-        private readonly IEglWindowGlPlatformSurfaceInfo _info;
-        
-        public EglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base()
-        {
-            _display = context.Display;
-            _context = context;
-            _info = info;
-        }
-
-        public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
-        {
-            var glSurface = _display.CreateWindowSurface(_info.Handle);
-            return new RenderTarget(_display, _context, glSurface, _info);
-        }
-
-        class RenderTarget : EglPlatformSurfaceRenderTargetBase
-        {
-            private readonly EglDisplay _display;
-            private readonly EglContext _context;
-            private readonly EglSurface _glSurface;
-            private readonly IEglWindowGlPlatformSurfaceInfo _info;
-            private PixelSize _initialSize;
-
-            public RenderTarget(EglDisplay display, EglContext context,
-                EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context)
-            {
-                _display = display;
-                _context = context;
-                _glSurface = glSurface;
-                _info = info;
-                _initialSize = info.Size;
-            }
-
-            public override void Dispose() => _glSurface.Dispose();
-
-            public override bool IsCorrupted => _initialSize != _info.Size;
-            
-            public override IGlPlatformSurfaceRenderingSession BeginDraw() => base.BeginDraw(_glSurface, _info);
-        }
-    }
-}
-

+ 13 - 0
src/Avalonia.OpenGL/GlInterface.cs

@@ -82,6 +82,9 @@ namespace Avalonia.OpenGL
         
         [GlEntryPoint("glFlush")]
         public Action Flush { get; }
+        
+        [GlEntryPoint("glFinish")]
+        public Action Finish { get; }
 
         public delegate IntPtr GlGetString(int v);
         [GlEntryPoint("glGetString")]
@@ -144,6 +147,10 @@ namespace Avalonia.OpenGL
         [GlEntryPoint("glBindTexture")]
         public GlBindTexture BindTexture { get; }
         
+        public delegate void GlActiveTexture(int texture);
+        [GlEntryPoint("glActiveTexture")]
+        public GlActiveTexture ActiveTexture { get; }
+        
         public delegate void GlDeleteTextures(int count, int[] textures);
         [GlEntryPoint("glDeleteTextures")]
         public GlDeleteTextures DeleteTextures { get; }
@@ -154,6 +161,12 @@ namespace Avalonia.OpenGL
         [GlEntryPoint("glTexImage2D")]
         public GlTexImage2D TexImage2D { get; }
 
+        public delegate void GlCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y,
+            int width, int height);
+        
+        [GlEntryPoint("glCopyTexSubImage2D")]
+        public GlCopyTexSubImage2D CopyTexSubImage2D { get; }
+
         public delegate void GlTexParameteri(int target, int name, int value);
         [GlEntryPoint("glTexParameteri")]
         public GlTexParameteri TexParameteri { get; }

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

@@ -9,5 +9,7 @@ namespace Avalonia.OpenGL
         int SampleCount { get; }
         int StencilSize { get; }
         IDisposable MakeCurrent();
+        IDisposable EnsureCurrent();
+        bool IsSharedWith(IGlContext context);
     }
 }

+ 1 - 1
src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs

@@ -4,6 +4,6 @@ namespace Avalonia.OpenGL
 {
     public interface IOpenGlAwarePlatformRenderInterface
     {
-        IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap();
+        IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi);
     }
 }

+ 13 - 0
src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs

@@ -0,0 +1,13 @@
+namespace Avalonia.OpenGL
+{
+    public interface IPlatformOpenGlInterface
+    {
+        IGlContext PrimaryContext { get; }
+        IGlContext CreateSharedContext();
+        bool CanShareContexts { get; }
+        bool CanCreateContexts { get; }
+        IGlContext CreateContext();
+        /*IGlContext TryCreateContext(GlVersion version);
+        */
+    }
+}

+ 0 - 8
src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs

@@ -1,8 +0,0 @@
-namespace Avalonia.OpenGL
-{
-    public interface IWindowingPlatformGlFeature
-    {
-        IGlContext CreateContext();
-        IGlContext MainContext { get; }
-    }
-}

+ 17 - 0
src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs

@@ -0,0 +1,17 @@
+using System;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
+
+namespace Avalonia.OpenGL.Imaging
+{
+    public interface IOpenGlBitmapImpl : IBitmapImpl
+    {
+        IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback);
+        bool SupportsContext(IGlContext context);
+    }
+
+    public interface IOpenGlBitmapAttachment : IDisposable
+    {
+        void Present();
+    }
+}

+ 0 - 13
src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs

@@ -1,13 +0,0 @@
-using System;
-using Avalonia.Media.Imaging;
-using Avalonia.Platform;
-
-namespace Avalonia.OpenGL.Imaging
-{
-    public interface IOpenGlTextureBitmapImpl : IBitmapImpl
-    {
-        IDisposable Lock();
-        void SetBackBuffer(int textureId, int internalFormat, PixelSize pixelSize, double dpiScaling);
-        void SetDirty();
-    }
-}

+ 16 - 18
src/Avalonia.OpenGL/Imaging/OpenGlTextureBitmap.cs → src/Avalonia.OpenGL/Imaging/OpenGlBitmap.cs

@@ -6,32 +6,30 @@ using Avalonia.Threading;
 
 namespace Avalonia.OpenGL.Imaging
 {
-    public class OpenGlTextureBitmap : Bitmap, IAffectsRender
+    public class OpenGlBitmap : Bitmap, IAffectsRender
     {
-        private IOpenGlTextureBitmapImpl _impl;
-        static IOpenGlTextureBitmapImpl CreateOrThrow()
+        private IOpenGlBitmapImpl _impl;
+
+        public OpenGlBitmap(PixelSize size, Vector dpi) 
+            : base(CreateOrThrow(size, dpi))
         {
-            if (!(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is IOpenGlAwarePlatformRenderInterface
-                glAware))
-                throw new PlatformNotSupportedException("Rendering platform does not support OpenGL integration");
-            return glAware.CreateOpenGlTextureBitmap();
+            _impl = (IOpenGlBitmapImpl)PlatformImpl.Item;
         }
         
-        public OpenGlTextureBitmap() 
-            : base(CreateOrThrow())
+        static IOpenGlBitmapImpl CreateOrThrow(PixelSize size, Vector dpi)
         {
-            _impl = (IOpenGlTextureBitmapImpl)PlatformImpl.Item;
+            if (!(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is IOpenGlAwarePlatformRenderInterface
+                glAware))
+                throw new PlatformNotSupportedException("Rendering platform does not support OpenGL integration");
+            return glAware.CreateOpenGlBitmap(size, dpi);
         }
 
-        public IDisposable Lock() => _impl.Lock();
+        public IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context) =>
+            _impl.CreateFramebufferAttachment(context, SetIsDirty);
 
-        public void SetTexture(int textureId, int internalFormat, PixelSize size, double dpiScaling)
-        {
-            _impl.SetBackBuffer(textureId, internalFormat, size, dpiScaling);
-            SetIsDirty();
-        }
-
-        public void SetIsDirty()
+        public bool SupportsContext(IGlContext context) => _impl.SupportsContext(context);
+        
+        void SetIsDirty()
         {
             if (Dispatcher.UIThread.CheckAccess())
                 CallInvalidated();

+ 1 - 0
src/Avalonia.OpenGL/OpenGlException.cs

@@ -1,4 +1,5 @@
 using System;
+using Avalonia.OpenGL.Egl;
 
 namespace Avalonia.OpenGL
 {

+ 1 - 1
src/Avalonia.OpenGL/IGlPlatformSurface.cs → src/Avalonia.OpenGL/Surfaces/IGlPlatformSurface.cs

@@ -1,4 +1,4 @@
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Surfaces
 {
     public interface IGlPlatformSurface
     {

+ 1 - 1
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderTarget.cs → src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs

@@ -1,6 +1,6 @@
 using System;
 
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Surfaces
 {
     public interface IGlPlatformSurfaceRenderTarget : IDisposable
     {

+ 1 - 1
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs → src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderingSession.cs

@@ -1,6 +1,6 @@
 using System;
 
-namespace Avalonia.OpenGL
+namespace Avalonia.OpenGL.Surfaces
 {
     public interface IGlPlatformSurfaceRenderingSession : IDisposable
     {

+ 41 - 12
src/Avalonia.X11/Glx/GlxContext.cs

@@ -8,18 +8,21 @@ namespace Avalonia.X11.Glx
     {
         public  IntPtr Handle { get; }
         public GlxInterface Glx { get; }
+        private readonly GlxContext _sharedWith;
         private readonly X11Info _x11;
         private readonly IntPtr _defaultXid;
         private readonly bool _ownsPBuffer;
         private readonly object _lock = new object();
 
-        public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display, 
+        public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display,
+            GlxContext sharedWith,
             GlVersion version, int sampleCount, int stencilSize,
             X11Info x11, IntPtr defaultXid,
             bool ownsPBuffer)
         {
             Handle = handle;
             Glx = glx;
+            _sharedWith = sharedWith;
             _x11 = x11;
             _defaultXid = defaultXid;
             _ownsPBuffer = ownsPBuffer;
@@ -37,25 +40,21 @@ namespace Avalonia.X11.Glx
         public int SampleCount { get; }
         public int StencilSize { get; }
         
-        public IDisposable Lock()
-        {
-            Monitor.Enter(_lock);
-            return Disposable.Create(() => Monitor.Exit(_lock));
-        }
-
         class RestoreContext : IDisposable
         {
             private GlxInterface _glx;
             private IntPtr _defaultDisplay;
+            private readonly object _l;
             private IntPtr _display;
             private IntPtr _context;
             private IntPtr _read;
             private IntPtr _draw;
 
-            public RestoreContext(GlxInterface glx, IntPtr defaultDisplay)
+            public RestoreContext(GlxInterface glx, IntPtr defaultDisplay, object l)
             {
                 _glx = glx;
                 _defaultDisplay = defaultDisplay;
+                _l = l;
                 _display = _glx.GetCurrentDisplay();
                 _context = _glx.GetCurrentContext();
                 _read = _glx.GetCurrentReadDrawable();
@@ -66,19 +65,49 @@ namespace Avalonia.X11.Glx
             {
                 var disp = _display == IntPtr.Zero ? _defaultDisplay : _display;
                 _glx.MakeContextCurrent(disp, _draw, _read, _context);
+                Monitor.Exit(_l);
             }
         }
         
         public IDisposable MakeCurrent() => MakeCurrent(_defaultXid);
+        public IDisposable EnsureCurrent()
+        {
+            if(IsCurrent)
+                return Disposable.Empty;
+            return MakeCurrent();
+        }
+
+        public bool IsSharedWith(IGlContext context)
+        {
+            var c = (GlxContext)context;
+            return c == this
+                   || c._sharedWith == this
+                   || _sharedWith == context
+                   || _sharedWith != null && _sharedWith == c._sharedWith;
+        }
 
         public IDisposable MakeCurrent(IntPtr xid)
         {
-            var old = new RestoreContext(Glx, _x11.Display);
-            if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle))
-                throw new OpenGlException("glXMakeContextCurrent failed ");
-            return old;
+            Monitor.Enter(_lock);
+            var success = false;
+            try
+            {
+                var old = new RestoreContext(Glx, _x11.Display, _lock);
+                if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle))
+                    throw new OpenGlException("glXMakeContextCurrent failed ");
+
+                success = true;
+                return old;
+            }
+            finally
+            {
+                if (!success)
+                    Monitor.Exit(_lock);
+            }
         }
 
+        public bool IsCurrent => Glx.GetCurrentContext() == Handle;
+
         public void Dispose()
         {
             Glx.DestroyContext(_x11.Display, Handle);

+ 4 - 4
src/Avalonia.X11/Glx/GlxDisplay.cs

@@ -113,9 +113,9 @@ namespace Avalonia.X11.Glx
         }
 
 
-        public GlxContext CreateContext() => CreateContext(DeferredContext);
-
-        GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
+        public GlxContext CreateContext() => CreateContext();
+        
+        public GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
             share.SampleCount, share.StencilSize, true);
         
         GlxContext CreateContext(IntPtr defaultXid, IGlContext share,
@@ -144,7 +144,7 @@ namespace Avalonia.X11.Glx
                     if (handle != IntPtr.Zero)
                     {
                         _version = profile;
-                        return new GlxContext(new GlxInterface(), handle, this, profile,
+                        return new GlxContext(new GlxInterface(), handle, this, (GlxContext)share, profile,
                             sampleCount, stencilSize, _x11, defaultXid, ownsPBuffer);
                         
                     }

+ 10 - 15
src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs

@@ -1,5 +1,8 @@
 using System;
 using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
+using Avalonia.OpenGL.Surfaces;
+using static Avalonia.OpenGL.GlConsts;
 
 namespace Avalonia.X11.Glx
 {
@@ -40,33 +43,26 @@ namespace Avalonia.X11.Glx
 
             public IGlPlatformSurfaceRenderingSession BeginDraw()
             {
-                var l = _context.Lock();
-                try
-                {
-
-                    return new Session(_context, _info, l, _context.MakeCurrent(_info.Handle));
-                }
-                catch
-                {
-                    l.Dispose();
-                    throw;
-                }
+                var oldContext = _context.MakeCurrent(_info.Handle);
+                
+                // Reset to default FBO first
+                _context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, 0);
+                    
+                return new Session(_context, _info, oldContext);
             }
             
             class Session : IGlPlatformSurfaceRenderingSession
             {
                 private readonly GlxContext _context;
                 private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
-                private IDisposable _lock;
                 private readonly IDisposable _clearContext;
                 public IGlContext Context => _context;
 
                 public Session(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info,
-                    IDisposable @lock, IDisposable clearContext)
+                    IDisposable clearContext)
                 {
                     _context = context;
                     _info = info;
-                    _lock = @lock;
                     _clearContext = clearContext;
                 }
 
@@ -77,7 +73,6 @@ namespace Avalonia.X11.Glx
                     _context.Display.SwapBuffers(_info.Handle);
                     _context.Glx.WaitX();
                     _clearContext.Dispose();
-                    _lock.Dispose();
                 }
 
                 public PixelSize Size => _info.Size;

+ 8 - 5
src/Avalonia.X11/Glx/GlxPlatformFeature.cs

@@ -5,31 +5,34 @@ using Avalonia.OpenGL;
 
 namespace Avalonia.X11.Glx
 {
-    class GlxGlPlatformFeature : IWindowingPlatformGlFeature
+    class GlxPlatformOpenGlInterface : IPlatformOpenGlInterface
     {
         public GlxDisplay Display { get; private set; }
+        public bool CanCreateContexts => true;
+        public bool CanShareContexts => true;
         public IGlContext CreateContext() => Display.CreateContext();
+        public IGlContext CreateSharedContext() => Display.CreateContext(PrimaryContext);
         public GlxContext DeferredContext { get; private set; }
-        public IGlContext MainContext => DeferredContext;
+        public IGlContext PrimaryContext => DeferredContext;
 
         public static bool TryInitialize(X11Info x11, IList<GlVersion> glProfiles)
         {
             var feature = TryCreate(x11, glProfiles);
             if (feature != null)
             {
-                AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature);
+                AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(feature);
                 return true;
             }
 
             return false;
         }
         
-        public static GlxGlPlatformFeature TryCreate(X11Info x11, IList<GlVersion> glProfiles)
+        public static GlxPlatformOpenGlInterface TryCreate(X11Info x11, IList<GlVersion> glProfiles)
         {
             try
             {
                 var disp = new GlxDisplay(x11, glProfiles);
-                return new GlxGlPlatformFeature
+                return new GlxPlatformOpenGlInterface
                 {
                     Display = disp,
                     DeferredContext = disp.DeferredContext

+ 3 - 2
src/Avalonia.X11/X11Platform.cs

@@ -7,6 +7,7 @@ using Avalonia.FreeDesktop;
 using Avalonia.Input;
 using Avalonia.Input.Platform;
 using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.X11;
@@ -70,9 +71,9 @@ namespace Avalonia.X11
             if (options.UseGpu)
             {
                 if (options.UseEGL)
-                    EglGlPlatformFeature.TryInitialize();
+                    EglPlatformOpenGlInterface.TryInitialize();
                 else
-                    GlxGlPlatformFeature.TryInitialize(Info, Options.GlProfiles);
+                    GlxPlatformOpenGlInterface.TryInitialize(Info, Options.GlProfiles);
             }
 
             

+ 5 - 4
src/Avalonia.X11/X11Window.cs

@@ -12,6 +12,7 @@ using Avalonia.FreeDesktop;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
 using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Threading;
@@ -65,7 +66,7 @@ namespace Avalonia.X11
             _touch = new TouchDevice();
             _keyboard = platform.KeyboardDevice;
 
-            var glfeature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
+            var glfeature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
             XSetWindowAttributes attr = new XSetWindowAttributes();
             var valueMask = default(SetWindowValuemask);
 
@@ -87,13 +88,13 @@ namespace Avalonia.X11
             // OpenGL seems to be do weird things to it's current window which breaks resize sometimes
             _useRenderWindow = glfeature != null;
             
-            var glx = glfeature as GlxGlPlatformFeature;
+            var glx = glfeature as GlxPlatformOpenGlInterface;
             if (glx != null)
                 visualInfo = *glx.Display.VisualInfo;
             else if (glfeature == null)
                 visualInfo = _x11.TransparentVisualInfo;
 
-            var egl = glfeature as EglGlPlatformFeature;
+            var egl = glfeature as EglPlatformOpenGlInterface;
             
             var visual = IntPtr.Zero;
             var depth = 24;
@@ -168,7 +169,7 @@ namespace Avalonia.X11
             
             if (egl != null)
                 surfaces.Insert(0,
-                    new EglGlPlatformSurface(egl.DeferredContext,
+                    new EglGlPlatformSurface(egl,
                         new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
             if (glx != null)
                 surfaces.Insert(0, new GlxGlPlatformSurface(glx.Display, glx.DeferredContext,

+ 2 - 2
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@@ -32,8 +32,8 @@ namespace Avalonia.LinuxFramebuffer
         void Initialize()
         {
             Threading = new InternalPlatformThreadingInterface();
-            if (_fb is IWindowingPlatformGlFeature glFeature)
-                AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(glFeature);
+            if (_fb is IGlOutputBackend gl)
+                AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(gl.PlatformOpenGlInterface);
             AvaloniaLocator.CurrentMutable
                 .Bind<IPlatformThreadingInterface>().ToConstant(Threading)
                 .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))

+ 11 - 7
src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs

@@ -4,6 +4,8 @@ using System.ComponentModel;
 using System.Linq;
 using System.Runtime.InteropServices;
 using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
+using Avalonia.OpenGL.Surfaces;
 using Avalonia.Platform.Interop;
 using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods;
 using static Avalonia.LinuxFramebuffer.Output.LibDrm;
@@ -11,13 +13,16 @@ using static Avalonia.LinuxFramebuffer.Output.LibDrm.GbmColorFormats;
 
 namespace Avalonia.LinuxFramebuffer.Output
 {
-    public unsafe class DrmOutput : IOutputBackend, IGlPlatformSurface, IWindowingPlatformGlFeature
+    public unsafe class DrmOutput : IGlOutputBackend, IGlPlatformSurface
     {
         private DrmCard _card;
         private readonly EglGlPlatformSurface _eglPlatformSurface;
         public PixelSize PixelSize => _mode.Resolution;
         public double Scaling { get; set; }
-        public IGlContext MainContext => _deferredContext;
+        public IGlContext PrimaryContext => _deferredContext;
+
+        private EglPlatformOpenGlInterface _platformGl;
+        public IPlatformOpenGlInterface PlatformOpenGlInterface => _platformGl;
 
         public DrmOutput(string path = null)
         {
@@ -132,10 +137,9 @@ namespace Avalonia.LinuxFramebuffer.Output
             if(_gbmTargetSurface == null)
                 throw new InvalidOperationException("Unable to create GBM surface");
 
-            
-            
-            _eglDisplay = new EglDisplay(new EglInterface(eglGetProcAddress), 0x31D7, device, null);
-            _eglSurface = _eglDisplay.CreateWindowSurface(_gbmTargetSurface);
+            _eglDisplay = new EglDisplay(new EglInterface(eglGetProcAddress), false, 0x31D7, device, null);
+            _platformGl = new EglPlatformOpenGlInterface(_eglDisplay);
+            _eglSurface =  _platformGl.CreateWindowSurface(_gbmTargetSurface);
 
 
             EglContext CreateContext(EglContext share)
@@ -144,7 +148,7 @@ namespace Avalonia.LinuxFramebuffer.Output
                     GbmBoFlags.GBM_BO_USE_RENDERING);
                 if (offSurf == null)
                     throw new InvalidOperationException("Unable to create 1x1 sized GBM surface");
-                return _eglDisplay.CreateContext(share, _eglDisplay.CreateWindowSurface(offSurf));
+                return _eglDisplay.CreateContext(share, _platformGl.CreateWindowSurface(offSurf));
             }
             
             _deferredContext = CreateContext(null);

+ 9 - 0
src/Linux/Avalonia.LinuxFramebuffer/Output/IGlOutputBackend.cs

@@ -0,0 +1,9 @@
+using Avalonia.OpenGL;
+
+namespace Avalonia.LinuxFramebuffer.Output
+{
+    public interface IGlOutputBackend : IOutputBackend
+    {
+        public IPlatformOpenGlInterface PlatformOpenGlInterface { get; }
+    }
+}

+ 1 - 1
src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs

@@ -19,6 +19,6 @@ namespace Avalonia.Skia
 
     public interface IOpenGlAwareSkiaGpu : ISkiaGpu
     {
-        IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap();
+        IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi);
     }
 }

+ 1 - 0
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Reactive.Disposables;
 using Avalonia.OpenGL;
+using Avalonia.OpenGL.Surfaces;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using SkiaSharp;

+ 6 - 3
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using Avalonia.OpenGL;
 using Avalonia.OpenGL.Imaging;
+using Avalonia.OpenGL.Surfaces;
 using SkiaSharp;
 
 namespace Avalonia.Skia
@@ -8,10 +9,12 @@ namespace Avalonia.Skia
     class GlSkiaGpu : IOpenGlAwareSkiaGpu
     {
         private GRContext _grContext;
+        private IGlContext _glContext;
 
-        public GlSkiaGpu(IWindowingPlatformGlFeature gl, long? maxResourceBytes)
+        public GlSkiaGpu(IPlatformOpenGlInterface openGl, long? maxResourceBytes)
         {
-            var context = gl.MainContext;
+            var context = openGl.PrimaryContext;
+            _glContext = context;
             using (context.MakeCurrent())
             {
                 using (var iface = context.Version.Type == GlProfileType.OpenGL ?
@@ -40,6 +43,6 @@ namespace Avalonia.Skia
             return null;
         }
 
-        public IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap() => new OpenGlTextureBitmapImpl();
+        public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi) => new GlOpenGlBitmapImpl(_glContext, size, dpi);
     }
 }

+ 207 - 0
src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs

@@ -0,0 +1,207 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Avalonia.OpenGL;
+using Avalonia.OpenGL.Imaging;
+using Avalonia.Utilities;
+using SkiaSharp;
+using static Avalonia.OpenGL.GlConsts;
+
+namespace Avalonia.Skia
+{
+    class GlOpenGlBitmapImpl : IOpenGlBitmapImpl, IDrawableBitmapImpl
+    {
+        private readonly IGlContext _context;
+        private readonly object _lock = new object();
+        private IGlPresentableOpenGlSurface _surface;
+
+        public GlOpenGlBitmapImpl(IGlContext context, PixelSize pixelSize, Vector dpi)
+        {
+            _context = context;
+            PixelSize = pixelSize;
+            Dpi = dpi;
+        }
+
+        public Vector Dpi { get; }
+        public PixelSize PixelSize { get; }
+        public int Version { get; private set; }
+        public void Save(string fileName) => throw new NotSupportedException();
+
+        public void Save(Stream stream) => throw new NotSupportedException();
+
+        public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
+        {
+            lock (_lock)
+            {
+                if (_surface == null)
+                    return;
+                using (_surface.Lock())
+                {
+                    using (var backendTexture = new GRBackendTexture(PixelSize.Width, PixelSize.Height, false,
+                        new GRGlTextureInfo(
+                            GlConsts.GL_TEXTURE_2D, (uint)_surface.GetTextureId(),
+                            (uint)_surface.InternalFormat)))
+                    using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.TopLeft,
+                        SKColorType.Rgba8888))
+                    {
+                        // Again, silently ignore, if something went wrong it's not our fault
+                        if (surface == null)
+                            return;
+
+                        using (var snapshot = surface.Snapshot())
+                            context.Canvas.DrawImage(snapshot, sourceRect, destRect, paint);
+                    }
+
+                }
+            }
+        }
+
+        public IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback)
+        {
+            if (!SupportsContext(context))
+                throw new OpenGlException("Context is not supported for texture sharing");
+            return new SharedOpenGlBitmapAttachment(this, context, presentCallback);
+        }
+
+        public bool SupportsContext(IGlContext context)
+        {
+            // TODO: negotiated platform surface sharing
+            return _context.IsSharedWith(context);
+        }
+
+        public void Dispose()
+        {
+
+        }
+
+        internal void Present(IGlPresentableOpenGlSurface surface)
+        {
+            lock (_lock)
+            {
+                _surface = surface;
+            }
+        }
+    }
+
+    interface IGlPresentableOpenGlSurface : IDisposable
+    {
+        int GetTextureId();
+        int InternalFormat { get; }
+        IDisposable Lock();
+    }
+
+    class SharedOpenGlBitmapAttachment : IOpenGlBitmapAttachment, IGlPresentableOpenGlSurface
+    {
+        private readonly GlOpenGlBitmapImpl _bitmap;
+        private readonly IGlContext _context;
+        private readonly Action _presentCallback;
+        private readonly int _fbo;
+        private readonly int _texture;
+        private readonly int _frontBuffer;
+        private bool _disposed;
+        private readonly DisposableLock _lock = new DisposableLock();
+
+        public SharedOpenGlBitmapAttachment(GlOpenGlBitmapImpl bitmap, IGlContext context, Action presentCallback)
+        {
+            _bitmap = bitmap;
+            _context = context;
+            _presentCallback = presentCallback;
+            using (_context.EnsureCurrent())
+            {
+                var glVersion = _context.Version;
+                InternalFormat = glVersion.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8;
+                
+                _context.GlInterface.GetIntegerv(GL_FRAMEBUFFER_BINDING, out _fbo);
+                if (_fbo == 0)
+                    throw new OpenGlException("Current FBO is 0");
+
+                {
+                    var gl = _context.GlInterface;
+                    
+                    var textures = new int[2];
+                    gl.GenTextures(2, textures);
+                    _texture = textures[0];
+                    _frontBuffer = textures[1];
+
+                    gl.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture);
+                    foreach (var t in textures)
+                    {
+                        gl.BindTexture(GL_TEXTURE_2D, t);
+                        gl.TexImage2D(GL_TEXTURE_2D, 0,
+                            InternalFormat,
+                            _bitmap.PixelSize.Width, _bitmap.PixelSize.Height,
+                            0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero);
+
+                        gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+                        gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+                    }
+
+                    gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
+                    gl.BindTexture(GL_TEXTURE_2D, oldTexture);
+                    
+                }
+            }
+        }
+
+        public void Present()
+        {
+            using (_context.MakeCurrent())
+            {
+                if (_disposed)
+                    throw new ObjectDisposedException(nameof(SharedOpenGlBitmapAttachment));
+                
+                var gl = _context.GlInterface;
+               
+                gl.Finish();
+                using (Lock())
+                {
+                    gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFbo);
+                    gl.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture);
+                    gl.GetIntegerv(GL_ACTIVE_TEXTURE, out var oldActive);
+                    
+                    gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo);
+                    gl.BindTexture(GL_TEXTURE_2D, _frontBuffer);
+                    gl.ActiveTexture(GL_TEXTURE0);
+
+                    gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _bitmap.PixelSize.Width,
+                        _bitmap.PixelSize.Height);
+
+                    gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo);
+                    gl.BindTexture(GL_TEXTURE_2D, oldTexture);
+                    gl.ActiveTexture(oldActive);
+                    
+                    gl.Finish();
+                }
+            }
+            
+            _bitmap.Present(this);
+            _presentCallback();
+        }
+
+        public void Dispose()
+        {
+            var gl = _context.GlInterface;
+            _bitmap.Present(null);
+            
+            if(_disposed)
+                return;
+            using (_context.MakeCurrent())
+            using (Lock())
+            {
+                if(_disposed)
+                    return;
+                _disposed = true;
+                gl.DeleteTextures(2, new[] { _texture, _frontBuffer });
+            }
+        }
+
+        int IGlPresentableOpenGlSurface.GetTextureId()
+        {
+            return _frontBuffer;
+        }
+
+        public int InternalFormat { get; }
+
+        public IDisposable Lock() => _lock.Lock();
+    }
+}

+ 0 - 81
src/Skia/Avalonia.Skia/Gpu/OpenGlTextureBitmapImpl.cs

@@ -1,81 +0,0 @@
-using System;
-using System.IO;
-using Avalonia.OpenGL;
-using Avalonia.OpenGL.Imaging;
-using Avalonia.Skia.Helpers;
-using Avalonia.Utilities;
-using SkiaSharp;
-
-namespace Avalonia.Skia
-{
-    class OpenGlTextureBitmapImpl : IOpenGlTextureBitmapImpl, IDrawableBitmapImpl
-    {
-        private DisposableLock _lock = new DisposableLock();
-        private int _textureId;
-        private int _internalFormat;
-
-        public void Dispose()
-        {
-            using (Lock())
-            {
-                _textureId = 0;
-                PixelSize = new PixelSize(1, 1);
-                Version++;
-            }
-        }
-
-        public Vector Dpi { get; private set; } = new Vector(96, 96);
-        public PixelSize PixelSize { get; private set; } = new PixelSize(1, 1);
-        public int Version { get; private set; } = 0;
-
-        public void Save(string fileName) => throw new System.NotSupportedException();
-        public void Save(Stream stream) => throw new System.NotSupportedException();
-
-        public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
-        {
-            // For now silently ignore
-            if (context.GrContext == null)
-                return;
-
-            using (Lock())
-            {
-                if (_textureId == 0)
-                    return;
-                using (var backendTexture = new GRBackendTexture(PixelSize.Width, PixelSize.Height, false,
-                    new GRGlTextureInfo(
-                        GlConsts.GL_TEXTURE_2D, (uint)_textureId,
-                        (uint)_internalFormat)))
-                using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.TopLeft,
-                    SKColorType.Rgba8888))
-                {
-                    // Again, silently ignore, if something went wrong it's not our fault
-                    if (surface == null)
-                        return;
-
-                    using (var snapshot = surface.Snapshot())
-                        context.Canvas.DrawImage(snapshot, sourceRect, destRect, paint);
-                }
-            }
-        }
-
-        public IDisposable Lock() => _lock.Lock();
-
-        public void SetBackBuffer(int textureId, int internalFormat, PixelSize pixelSize, double dpiScaling)
-        {
-            using (_lock.Lock())
-            {
-                _textureId = textureId;
-                _internalFormat = internalFormat;
-                PixelSize = pixelSize;
-                Dpi = new Vector(96 * dpiScaling, 96 * dpiScaling);
-                Version++;
-            }
-        }
-
-        public void SetDirty()
-        {
-            using (_lock.Lock())
-                Version++;
-        }
-    }
-}

+ 3 - 3
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@@ -30,7 +30,7 @@ namespace Avalonia.Skia
                 return;
             }
 
-            var gl = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
+            var gl = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
             if (gl != null) 
                 _skiaGpu = new GlSkiaGpu(gl, maxResourceBytes);
         }
@@ -256,10 +256,10 @@ namespace Avalonia.Skia
 
         }
 
-        public IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap()
+        public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi)
         {
             if (_skiaGpu is IOpenGlAwareSkiaGpu glAware)
-                return glAware.CreateOpenGlTextureBitmap();
+                return glAware.CreateOpenGlBitmap(size, dpi);
             if (_skiaGpu == null)
                 throw new PlatformNotSupportedException("GPU acceleration is not available");
             throw new PlatformNotSupportedException(

+ 5 - 4
src/Windows/Avalonia.Win32/Win32GlManager.cs

@@ -1,26 +1,27 @@
 using Avalonia.OpenGL;
 using Avalonia.OpenGL.Angle;
+using Avalonia.OpenGL.Egl;
 
 namespace Avalonia.Win32
 {
     static class Win32GlManager
     {
         /// <summary>This property is initialized if drawing platform requests OpenGL support</summary>
-        public static EglGlPlatformFeature EglFeature { get; private set; }
+        public static EglPlatformOpenGlInterface EglPlatformInterface { get; private set; }
 
         private static bool s_attemptedToInitialize;
 
         public static void Initialize()
         {
-            AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToFunc(() =>
+            AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToFunc(() =>
             {
                 if (!s_attemptedToInitialize)
                 {
-                    EglFeature = EglGlPlatformFeature.TryCreate(() => new AngleWin32EglDisplay());
+                    EglPlatformInterface = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
                     s_attemptedToInitialize = true;
                 }
 
-                return EglFeature;
+                return EglPlatformInterface;
             });
         }
     }

+ 4 - 2
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -7,6 +7,8 @@ using Avalonia.Controls.Platform;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
 using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
+using Avalonia.OpenGL.Surfaces;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Win32.Input;
@@ -103,8 +105,8 @@ namespace Avalonia.Win32
             CreateWindow();
             _framebuffer = new FramebufferManager(_hwnd);
 
-            if (Win32GlManager.EglFeature != null)
-                _gl = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this);
+            if (Win32GlManager.EglPlatformInterface != null)
+                _gl = new EglGlPlatformSurface(Win32GlManager.EglPlatformInterface, this);
 
             Screen = new ScreenImpl();
 

+ 16 - 4
src/iOS/Avalonia.iOS/EaglDisplay.cs

@@ -1,15 +1,18 @@
 using System;
+using System.Reactive.Disposables;
 using Avalonia.OpenGL;
 using OpenGLES;
 using OpenTK.Graphics.ES30;
 
 namespace Avalonia.iOS
 {
-    class EaglFeature : IWindowingPlatformGlFeature
+    class EaglFeature : IPlatformOpenGlInterface
     {
+        public IGlContext PrimaryContext => Context;
+        public IGlContext CreateSharedContext() => throw new NotSupportedException();
+        public bool CanShareContexts => false;
+        public bool CanCreateContexts => false;
         public IGlContext CreateContext() => throw new System.NotSupportedException();
-
-        public IGlContext MainContext => Context;
         public GlContext Context { get; } = new GlContext();
     }
 
@@ -61,9 +64,18 @@ namespace Avalonia.iOS
             return new ResetContext(old);
         }
 
+        public IDisposable EnsureCurrent()
+        {
+            if(EAGLContext.CurrentContext == Context)
+                return Disposable.Empty;
+            return MakeCurrent();
+        }
+
+        public bool IsSharedWith(IGlContext context) => false;
+
         public GlVersion Version { get; } = new GlVersion(GlProfileType.OpenGLES, 3, 0);
         public GlInterface GlInterface { get; }
         public int SampleCount { get; } = 0;
         public int StencilSize { get; } = 9;
     }
-}
+}

+ 2 - 1
src/iOS/Avalonia.iOS/EaglLayerSurface.cs

@@ -2,6 +2,7 @@
 using System;
 using System.Threading;
 using Avalonia.OpenGL;
+using Avalonia.OpenGL.Surfaces;
 using CoreAnimation;
 using OpenTK.Graphics.ES30;
 
@@ -91,4 +92,4 @@ namespace Avalonia.iOS
             }
         }
     }
-}
+}

+ 1 - 1
src/iOS/Avalonia.iOS/Platform.cs

@@ -26,7 +26,7 @@ namespace Avalonia.iOS
             var keyboard = new KeyboardDevice();
             var softKeyboard = new SoftKeyboardHelper();
             AvaloniaLocator.CurrentMutable
-                .Bind<IWindowingPlatformGlFeature>().ToConstant(GlFeature)
+                .Bind<IPlatformOpenGlInterface>().ToConstant(GlFeature)
                 .Bind<IStandardCursorFactory>().ToConstant(new CursorFactoryStub())
                 .Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformStub())
                 .Bind<IClipboard>().ToConstant(new ClipboardImpl())