Browse Source

Refactored GL version and context management

Nikita Tsukanov 5 years ago
parent
commit
e2bde7155f

+ 7 - 1
samples/ControlCatalog.NetCore/Program.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.Diagnostics;
 using System.Globalization;
 using System.Linq;
@@ -6,6 +7,7 @@ using System.Threading;
 using Avalonia;
 using Avalonia.ReactiveUI;
 using Avalonia.Dialogs;
+using Avalonia.OpenGL;
 
 namespace ControlCatalog.NetCore
 {
@@ -58,7 +60,11 @@ namespace ControlCatalog.NetCore
                 .With(new X11PlatformOptions
                 {
                     EnableMultiTouch = true,
-                    UseDBusMenu = true
+                    UseDBusMenu = true,
+                    GlProfiles = new List<GlVersion>
+                    {
+                        new GlVersion(GlProfileType.OpenGLES, 2, 0)
+                    }
                 })
                 .With(new Win32PlatformOptions
                 {

+ 3 - 3
samples/ControlCatalog/Pages/OpenGlPage.xaml.cs

@@ -91,12 +91,12 @@ namespace ControlCatalog.Pages
 
         private string GetShader(bool fragment, string shader)
         {
-            var version = (DisplayType == GlDisplayType.OpenGl ?
+            var version = (GlVersion.Type == GlProfileType.OpenGL ?
                 RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 150 : 120 :
                 100);
             var data = "#version " + version + "\n";
-            if (DisplayType == GlDisplayType.OpenGLES)
-                data += "precision mediump float\n";
+            if (GlVersion.Type == GlProfileType.OpenGLES)
+                data += "precision mediump float;\n";
             if (version >= 150)
             {
                 shader = shader.Replace("attribute", "in");

+ 36 - 20
src/Avalonia.Native/GlPlatformFeature.cs

@@ -16,21 +16,30 @@ namespace Avalonia.Native
             var immediate = display.CreateContext(null);
             var deferred = display.CreateContext(immediate);
             GlDisplay = new GlDisplay(display, immediate.SampleCount, immediate.StencilSize);
-            
-            ImmediateContext = new GlContext(GlDisplay, immediate);
-            DeferredContext = new GlContext(GlDisplay, deferred);
+
+            int major, minor;
+            using (immediate.MakeCurrent())
+            {
+                GlDisplay.GlInterface.GetIntegerv(GlConsts.GL_MAJOR_VERSION, out major);
+                GlDisplay.GlInterface.GetIntegerv(GlConsts.GL_MINOR_VERSION, out minor);
+            }
+
+            _version = new GlVersion(GlProfileType.OpenGL, major, minor);
+            ImmediateContext = new GlContext(GlDisplay, immediate, _version);
+            DeferredContext = new GlContext(GlDisplay, deferred, _version);
         }
 
-        public IGlContext ImmediateContext { get; }
+        internal IGlContext ImmediateContext { get; }
+        public IGlContext MainContext => DeferredContext;
         internal GlContext DeferredContext { get; }
         internal GlDisplay GlDisplay;
-        public IGlDisplay Display => GlDisplay;
+        private readonly GlVersion _version;
 
         public IGlContext CreateContext() => new GlContext(GlDisplay,
-            _display.CreateContext(((GlContext)ImmediateContext).Context));
+            _display.CreateContext(((GlContext)ImmediateContext).Context), _version);
     }
 
-    class GlDisplay : IGlDisplay
+    class GlDisplay
     {
         private readonly IAvnGlDisplay _display;
 
@@ -48,8 +57,6 @@ namespace Avalonia.Native
             });
         }
 
-        public GlDisplayType Type => GlDisplayType.OpenGl;
-
         public GlInterface GlInterface { get; }
 
         public int SampleCount { get; }
@@ -61,16 +68,20 @@ namespace Avalonia.Native
 
     class GlContext : IGlContext
     {
+        private readonly GlDisplay _display;
         public IAvnGlContext Context { get; private set; }
 
-        public GlContext(GlDisplay display, IAvnGlContext context)
+        public GlContext(GlDisplay display, IAvnGlContext context, GlVersion version)
         {
-            Display = display;
+            _display = display;
             Context = context;
+            Version = version;
         }
 
-        public IGlDisplay Display { get; }
-
+        public GlVersion Version { get; }
+        public GlInterface GlInterface => _display.GlInterface;
+        public int SampleCount => _display.SampleCount;
+        public int StencilSize => _display.StencilSize;
         public IDisposable MakeCurrent() => Context.MakeCurrent();
 
         public void Dispose()
@@ -84,15 +95,18 @@ namespace Avalonia.Native
     class GlPlatformSurfaceRenderTarget : IGlPlatformSurfaceRenderTarget
     {
         private IAvnGlSurfaceRenderTarget _target;
-        public GlPlatformSurfaceRenderTarget(IAvnGlSurfaceRenderTarget target)
+        private readonly IGlContext _context;
+
+        public GlPlatformSurfaceRenderTarget(IAvnGlSurfaceRenderTarget target, IGlContext context)
         {
             _target = target;
+            _context = context;
         }
 
         public IGlPlatformSurfaceRenderingSession BeginDraw()
         {
             var feature = (GlPlatformFeature)AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
-            return new GlPlatformSurfaceRenderingSession(feature.GlDisplay, _target.BeginDrawing());
+            return new GlPlatformSurfaceRenderingSession(_context, _target.BeginDrawing());
         }
 
         public void Dispose()
@@ -106,13 +120,13 @@ namespace Avalonia.Native
     {
         private IAvnGlSurfaceRenderingSession _session;
 
-        public GlPlatformSurfaceRenderingSession(GlDisplay display, IAvnGlSurfaceRenderingSession session)
+        public GlPlatformSurfaceRenderingSession(IGlContext context, IAvnGlSurfaceRenderingSession session)
         {
-            Display = display;
+            Context = context;
             _session = session;
         }
 
-        public IGlDisplay Display { get; }
+        public IGlContext Context { get; }
 
         public PixelSize Size
         {
@@ -138,14 +152,16 @@ namespace Avalonia.Native
     class GlPlatformSurface : IGlPlatformSurface
     {
         private readonly IAvnWindowBase _window;
+        private readonly IGlContext _context;
 
-        public GlPlatformSurface(IAvnWindowBase window)
+        public GlPlatformSurface(IAvnWindowBase window, IGlContext context)
         {
             _window = window;
+            _context = context;
         }
         public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
         {
-            return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget());
+            return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget(), _context);
         }
 
     }

+ 2 - 1
src/Avalonia.Native/PopupImpl.cs

@@ -24,7 +24,8 @@ namespace Avalonia.Native
             _glFeature = glFeature;
             using (var e = new PopupEvents(this))
             {
-                Init(factory.CreatePopup(e, _opts.UseGpu ? glFeature?.DeferredContext.Context : null), factory.CreateScreens());
+                var context = _opts.UseGpu ? glFeature?.DeferredContext : null;
+                Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context);
             }
             PopupPositioner = new ManagedPopupPositioner(new OsxManagedPopupPositionerPopupImplHelper(parent, MoveResize));
         }

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

@@ -5,6 +5,7 @@ using System;
 using Avalonia.Controls;
 using Avalonia.Controls.Platform;
 using Avalonia.Native.Interop;
+using Avalonia.OpenGL;
 using Avalonia.Platform;
 using Avalonia.Platform.Interop;
 
@@ -24,8 +25,8 @@ namespace Avalonia.Native
             _glFeature = glFeature;
             using (var e = new WindowEvents(this))
             {
-                Init(_native = factory.CreateWindow(e,
-                    _opts.UseGpu ? glFeature?.DeferredContext.Context : null), factory.CreateScreens());
+                var context = _opts.UseGpu ? glFeature?.DeferredContext : null;
+                Init(_native = factory.CreateWindow(e, context?.Context), factory.CreateScreens(), context);
             }
 
             NativeMenuExporter = new AvaloniaNativeMenuExporter(_native, factory);

+ 4 - 2
src/Avalonia.Native/WindowImplBase.cs

@@ -59,6 +59,7 @@ namespace Avalonia.Native
         private Size _lastRenderedLogicalSize;
         private double _savedScaling;
         private GlPlatformSurface _glSurface;
+        private IGlContext _glContext;
 
         internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, GlPlatformFeature glFeature)
         {
@@ -70,13 +71,14 @@ namespace Avalonia.Native
             _cursorFactory = AvaloniaLocator.Current.GetService<IStandardCursorFactory>();
         }
 
-        protected void Init(IAvnWindowBase window, IAvnScreens screens)
+        protected void Init(IAvnWindowBase window, IAvnScreens screens, IGlContext glContext)
         {
             _native = window;
+            _glContext = glContext;
 
             Handle = new MacOSTopLevelWindowHandle(window);
             if (_gpu)
-                _glSurface = new GlPlatformSurface(window);
+                _glSurface = new GlPlatformSurface(window, _glContext);
             Screen = new ScreenImpl(screens);
             _savedLogicalSize = ClientSize;
             _savedScaling = Scaling;

+ 12 - 3
src/Avalonia.OpenGL/EglContext.cs

@@ -11,17 +11,26 @@ namespace Avalonia.OpenGL
         private readonly EglInterface _egl;
         private readonly object _lock = new object();
 
-        public EglContext(EglDisplay display, EglInterface egl, IntPtr ctx, EglSurface offscreenSurface)
+        public EglContext(EglDisplay display, EglInterface egl, IntPtr ctx, EglSurface offscreenSurface,
+            GlVersion version, int sampleCount, int stencilSize)
         {
             _disp = display;
             _egl = egl;
             Context = ctx;
             OffscreenSurface = offscreenSurface;
+            Version = version;
+            SampleCount = sampleCount;
+            StencilSize = stencilSize;
+            GlInterface = GlInterface.FromNativeUtf8GetProcAddress(b => _egl.GetProcAddress(b));
         }
 
         public IntPtr Context { get; }
         public EglSurface OffscreenSurface { get; }
-        public IGlDisplay Display => _disp;
+        public GlVersion Version { get; }
+        public GlInterface GlInterface { get; }
+        public int SampleCount { get; }
+        public int StencilSize { get; }
+        public EglDisplay Display => _disp;
 
         public IDisposable Lock()
         {
@@ -52,7 +61,7 @@ namespace Avalonia.OpenGL
             }
 
         }
-        
+
         public IDisposable MakeCurrent()
         {
             var old = new RestoreContext(_egl, _disp.Handle);

+ 11 - 34
src/Avalonia.OpenGL/EglDisplay.cs

@@ -6,7 +6,7 @@ using static Avalonia.OpenGL.EglConsts;
 
 namespace Avalonia.OpenGL
 {
-    public class EglDisplay : IGlDisplay
+    public class EglDisplay
     {
         private readonly EglInterface _egl;
         private readonly IntPtr _display;
@@ -16,6 +16,9 @@ namespace Avalonia.OpenGL
 
         public IntPtr Handle => _display;
         private AngleOptions.PlatformApi? _angleApi;
+        private int _sampleCount;
+        private int _stencilSize;
+        private GlVersion _version;
 
         public EglDisplay(EglInterface egl) : this(egl, -1, IntPtr.Zero, null)
         {
@@ -76,13 +79,6 @@ namespace Avalonia.OpenGL
 
             foreach (var cfg in new[]
             {
-                new
-                {
-                    Attributes = new[] {EGL_NONE},
-                    Api = EGL_OPENGL_API,
-                    RenderableTypeBit = EGL_OPENGL_BIT,
-                    Type = GlDisplayType.OpenGl
-                },
                 new
                 {
                     Attributes = new[]
@@ -92,7 +88,7 @@ namespace Avalonia.OpenGL
                     },
                     Api = EGL_OPENGL_ES_API,
                     RenderableTypeBit = EGL_OPENGL_ES2_BIT,
-                    Type = GlDisplayType.OpenGLES
+                    Version = new GlVersion(GlProfileType.OpenGLES, 2, 0)
                 }
             })
             {
@@ -120,7 +116,9 @@ namespace Avalonia.OpenGL
                         continue;
                     _contextAttributes = cfg.Attributes;
                     _surfaceType = surfaceType;
-                    Type = cfg.Type;
+                    _version = cfg.Version;
+                    _egl.GetConfigAttrib(_display, _config, EGL_SAMPLES, out _sampleCount);
+                    _egl.GetConfigAttrib(_display, _config, EGL_STENCIL_SIZE, out _stencilSize);
                     goto Found;
                 }
 
@@ -128,8 +126,6 @@ namespace Avalonia.OpenGL
             Found:
             if (_contextAttributes == null)
                 throw new OpenGlException("No suitable EGL config was found");
-               
-            GlInterface = GlInterface.FromNativeUtf8GetProcAddress(b => _egl.GetProcAddress(b));
         }
 
         public EglDisplay() : this(new EglInterface())
@@ -137,8 +133,6 @@ namespace Avalonia.OpenGL
             
         }
         
-        public GlDisplayType Type { get; }
-        public GlInterface GlInterface { get; }
         public EglInterface EglInterface => _egl;
         public EglContext CreateContext(IGlContext share)
         {
@@ -156,7 +150,8 @@ 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, ctx, new EglSurface(this, _egl, surf),
+                _version, _sampleCount, _stencilSize);
             return rv;
         }
 
@@ -165,7 +160,7 @@ namespace Avalonia.OpenGL
             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);
+            var rv = new EglContext(this, _egl, ctx, offscreenSurface, _version, _sampleCount, _stencilSize);
             rv.MakeCurrent(null);
             return rv;
         }
@@ -177,23 +172,5 @@ namespace Avalonia.OpenGL
                 throw OpenGlException.GetFormattedException("eglCreateWindowSurface", _egl);
             return new EglSurface(this, _egl, s);
         }
-
-        public int SampleCount
-        {
-            get
-            {
-                _egl.GetConfigAttrib(_display, _config, EGL_SAMPLES, out var rv);
-                return rv;
-            }
-        }
-
-        public int StencilSize
-        {
-            get
-            {
-                _egl.GetConfigAttrib(_display, _config, EGL_STENCIL_SIZE, out var rv);
-                return rv;
-            }
-        }
     }
 }

+ 3 - 2
src/Avalonia.OpenGL/EglGlPlatformFeature.cs

@@ -6,12 +6,13 @@ namespace Avalonia.OpenGL
     public class EglGlPlatformFeature : IWindowingPlatformGlFeature
     {
         private EglDisplay _display;
-        public IGlDisplay Display => _display;
+        public EglDisplay Display => _display;
         public IGlContext CreateContext()
         {
             return _display.CreateContext(DeferredContext);
         }
-        public EglContext DeferredContext { get; set; }
+        public EglContext DeferredContext { get; private set; }
+        public IGlContext MainContext => DeferredContext;
 
         public static void TryInitialize()
         {

+ 4 - 4
src/Avalonia.OpenGL/EglGlPlatformSurface.cs

@@ -16,9 +16,9 @@ namespace Avalonia.OpenGL
         private readonly EglContext _context;
         private readonly IEglWindowGlPlatformSurfaceInfo _info;
         
-        public EglGlPlatformSurface(EglDisplay display, EglContext context, IEglWindowGlPlatformSurfaceInfo info)
+        public EglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info)
         {
-            _display = display;
+            _display = context.Display;
             _context = context;
             _info = info;
         }
@@ -96,7 +96,7 @@ namespace Avalonia.OpenGL
 
                 public void Dispose()
                 {
-                    _context.Display.GlInterface.Flush();
+                    _context.GlInterface.Flush();
                     _display.EglInterface.WaitGL();
                     _glSurface.SwapBuffers();
                     _display.EglInterface.WaitClient();
@@ -106,7 +106,7 @@ namespace Avalonia.OpenGL
                     _lock.Dispose();
                 }
 
-                public IGlDisplay Display => _context.Display;
+                public IGlContext Context => _context;
                 public PixelSize Size => _info.Size;
                 public double Scaling => _info.Scaling;
                 public bool IsYFlipped { get; }

+ 1 - 3
src/Avalonia.OpenGL/EglSurface.cs

@@ -21,8 +21,6 @@ namespace Avalonia.OpenGL
         }
 
         public override bool IsInvalid => handle == IntPtr.Zero;
-
-        public IGlDisplay Display => _display;
         public void SwapBuffers() => _egl.SwapBuffers(_display.Handle, handle);
     }
-}
+}

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

@@ -1,8 +0,0 @@
-namespace Avalonia.OpenGL
-{
-    public enum GlDisplayType
-    {
-        OpenGl,
-        OpenGLES
-    }
-}

+ 22 - 0
src/Avalonia.OpenGL/GlVersion.cs

@@ -0,0 +1,22 @@
+namespace Avalonia.OpenGL
+{
+    public enum GlProfileType
+    {
+        OpenGL,
+        OpenGLES
+    }
+    
+    public struct GlVersion
+    {
+        public GlProfileType Type { get; }
+        public int Major { get; }
+        public int Minor { get; }
+
+        public GlVersion(GlProfileType type, int major, int minor)
+        {
+            Type = type;
+            Major = major;
+            Minor = minor;
+        }
+    }
+}

+ 4 - 1
src/Avalonia.OpenGL/IGlContext.cs

@@ -4,7 +4,10 @@ namespace Avalonia.OpenGL
 {
     public interface IGlContext : IDisposable
     {
-        IGlDisplay Display { get; }
+        GlVersion Version { get; }
+        GlInterface GlInterface { get; }
+        int SampleCount { get; }
+        int StencilSize { get; }
         IDisposable MakeCurrent();
     }
 }

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

@@ -1,10 +0,0 @@
-namespace Avalonia.OpenGL
-{
-    public interface IGlDisplay
-    {
-        GlDisplayType Type { get; }
-        GlInterface GlInterface { get; }
-        int SampleCount { get; }
-        int StencilSize { get; }
-    }
-}

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

@@ -4,7 +4,7 @@ namespace Avalonia.OpenGL
 {
     public interface IGlPlatformSurfaceRenderingSession : IDisposable
     {
-        IGlDisplay Display { get; }
+        IGlContext Context { get; }
         PixelSize Size { get; }
         double Scaling { get; }
         bool IsYFlipped { get; }

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

@@ -2,7 +2,7 @@ namespace Avalonia.OpenGL
 {
     public interface IWindowingPlatformGlFeature
     {
-        IGlDisplay Display { get; }
         IGlContext CreateContext();
+        IGlContext MainContext { get; }
     }
 }

+ 9 - 9
src/Avalonia.OpenGL/OpenGlControlBase.cs

@@ -16,7 +16,7 @@ namespace Avalonia.OpenGL
         private OpenGlTextureBitmap _bitmap;
         private PixelSize _oldSize;
         private bool _glFailed;
-        protected GlDisplayType DisplayType { get; private set; }
+        protected GlVersion GlVersion { get; private set; }
         public sealed override void Render(DrawingContext context)
         {
             if(!EnsureInitialized())
@@ -26,7 +26,7 @@ namespace Avalonia.OpenGL
             {
                 using (_bitmap.Lock())
                 {
-                    var gl = _context.Display.GlInterface;
+                    var gl = _context.GlInterface;
                     gl.BindFramebuffer(GL_FRAMEBUFFER, _fb);
                     if (_oldSize != GetPixelSize())
                         ResizeTexture(gl);
@@ -46,7 +46,7 @@ namespace Avalonia.OpenGL
             {
                 using (_context.MakeCurrent())
                 {
-                    var gl = _context.Display.GlInterface;
+                    var gl = _context.GlInterface;
                     gl.BindTexture(GL_TEXTURE_2D, 0);
                     gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
                     gl.DeleteFramebuffers(1, new[] { _fb });
@@ -59,7 +59,7 @@ namespace Avalonia.OpenGL
                     try
                     {
                         if (callUserDeinit)
-                            OnOpenGlDeinit(_context.Display.GlInterface, _fb);
+                            OnOpenGlDeinit(_context.GlInterface, _fb);
                     }
                     finally
                     {
@@ -100,7 +100,7 @@ namespace Avalonia.OpenGL
                 return false;
             }
 
-            DisplayType = feature.Display.Type;
+            GlVersion = _context.Version;
             try
             {
                 _bitmap = new OpenGlTextureBitmap();
@@ -120,7 +120,7 @@ namespace Avalonia.OpenGL
                 try
                 {
                     _oldSize = GetPixelSize();
-                    var gl = _context.Display.GlInterface;
+                    var gl = _context.GlInterface;
                     var oneArr = new int[1];
                     gl.GenFramebuffers(1, oneArr);
                     _fb = oneArr[0];
@@ -153,7 +153,7 @@ namespace Avalonia.OpenGL
                 }
 
                 if (!_glFailed)
-                    OnOpenGlInit(_context.Display.GlInterface, _fb);
+                    OnOpenGlInit(_context.GlInterface, _fb);
             }
 
             if (_glFailed)
@@ -171,7 +171,7 @@ namespace Avalonia.OpenGL
             gl.GetIntegerv( GL_TEXTURE_BINDING_2D, out var oldTexture);
             gl.BindTexture(GL_TEXTURE_2D, _texture);
             gl.TexImage2D(GL_TEXTURE_2D, 0, 
-                DisplayType == GlDisplayType.OpenGLES ? GL_RGBA : GL_RGBA8,
+                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);
@@ -184,7 +184,7 @@ namespace Avalonia.OpenGL
             _renderBuffer = oneArr[0];
             gl.BindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
             gl.RenderbufferStorage(GL_RENDERBUFFER,
-                DisplayType == GlDisplayType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT,
+                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);

+ 13 - 1
src/Avalonia.X11/Glx/Glx.cs

@@ -97,12 +97,16 @@ namespace Avalonia.X11.Glx
         
         [GlEntryPointAttribute("glXWaitGL")]
         public GlxWaitGL WaitGL { get; }
-        public delegate  void GlxWaitGL();
+        public delegate void GlxWaitGL();
         
         public delegate int GlGetError();
         [GlEntryPoint("glGetError")]
         public GlGetError GetError { get; }
 
+        public delegate IntPtr GlxQueryExtensionsString(IntPtr display, int screen);
+        [GlEntryPoint("glXQueryExtensionsString")]
+        public GlxQueryExtensionsString QueryExtensionsString { get; }
+
         public GlxInterface() : base(SafeGetProcAddress)
         {
         }
@@ -122,5 +126,13 @@ namespace Avalonia.X11.Glx
         }
 
         private static readonly Func<string, bool, IntPtr> GlxConverted = ConvertNative(GlxGetProcAddress);
+
+        public string[] GetExtensions(IntPtr display)
+        {
+            var s = Marshal.PtrToStringAnsi(QueryExtensionsString(display, 0));
+            return s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
+                .Select(x => x.Trim()).ToArray();
+
+        }
     }
 }

+ 2 - 0
src/Avalonia.X11/Glx/GlxConsts.cs

@@ -100,6 +100,8 @@ namespace Avalonia.X11.Glx
         public const int GLX_CONTEXT_FLAGS_ARB = 0x2094;
         public const int GLX_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001;
         public const int GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002;
+        public const int GLX_CONTEXT_ES2_PROFILE_BIT_EXT = 0x00000004;
+        
         public const int GLX_CONTEXT_PROFILE_MASK_ARB = 0x9126;
 
     }

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

@@ -13,7 +13,9 @@ namespace Avalonia.X11.Glx
         private readonly bool _ownsPBuffer;
         private readonly object _lock = new object();
 
-        public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display, X11Info x11, IntPtr defaultXid,
+        public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display, 
+            GlVersion version, int sampleCount, int stencilSize,
+            X11Info x11, IntPtr defaultXid,
             bool ownsPBuffer)
         {
             Handle = handle;
@@ -22,10 +24,17 @@ namespace Avalonia.X11.Glx
             _defaultXid = defaultXid;
             _ownsPBuffer = ownsPBuffer;
             Display = display;
+            Version = version;
+            SampleCount = sampleCount;
+            StencilSize = stencilSize;
+            GlInterface = new GlInterface(GlxInterface.SafeGetProcAddress);
         }
         
         public GlxDisplay Display { get; }
-        IGlDisplay IGlContext.Display => Display;
+        public GlVersion Version { get; }
+        public GlInterface GlInterface { get; }
+        public int SampleCount { get; }
+        public int StencilSize { get; }
         
         public IDisposable Lock()
         {
@@ -58,7 +67,7 @@ namespace Avalonia.X11.Glx
                 _glx.MakeContextCurrent(disp, _draw, _read, _context);
             }
         }
-
+        
         public IDisposable MakeCurrent() => MakeCurrent(_defaultXid);
 
         public IDisposable MakeCurrent(IntPtr xid)

+ 67 - 32
src/Avalonia.X11/Glx/GlxDisplay.cs

@@ -1,27 +1,28 @@
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using Avalonia.OpenGL;
 using static Avalonia.X11.Glx.GlxConsts;
 
 namespace Avalonia.X11.Glx
 {
-    unsafe class GlxDisplay : IGlDisplay
+    unsafe class GlxDisplay
     {
         private readonly X11Info _x11;
+        private readonly List<GlVersion> _probeProfiles;
         private readonly IntPtr _fbconfig;
         private readonly XVisualInfo* _visual;
-        public GlDisplayType Type => GlDisplayType.OpenGl;
-        public GlInterface GlInterface { get; }
+        private string[] _displayExtensions;
+        private GlVersion? _version;
         
         public XVisualInfo* VisualInfo => _visual;
-        public int SampleCount { get; }
-        public int StencilSize { get; }
-        
         public GlxContext DeferredContext { get; }
         public GlxInterface Glx { get; } = new GlxInterface();
-        public GlxDisplay(X11Info x11) 
+        public GlxDisplay(X11Info x11, List<GlVersion> probeProfiles) 
         {
             _x11 = x11;
+            _probeProfiles = probeProfiles.ToList();
+            _displayExtensions = Glx.GetExtensions(_x11.Display);
 
             var baseAttribs = new[]
             {
@@ -37,7 +38,8 @@ namespace Avalonia.X11.Glx
                 GLX_STENCIL_SIZE, 8,
 
             };
-
+            int sampleCount = 0;
+            int stencilSize = 0;
             foreach (var attribs in new[]
             {
                 //baseAttribs.Concat(multiattribs),
@@ -70,9 +72,9 @@ namespace Avalonia.X11.Glx
             if (_visual == null)
                 throw new OpenGlException("Unable to get visual info from FBConfig");
             if (Glx.GetFBConfigAttrib(_x11.Display, _fbconfig, GLX_SAMPLES, out var samples) == 0)
-                SampleCount = samples;
+                sampleCount = samples;
             if (Glx.GetFBConfigAttrib(_x11.Display, _fbconfig, GLX_STENCIL_SIZE, out var stencil) == 0)
-                StencilSize = stencil;
+                stencilSize = stencil;
 
             var pbuffers = Enumerable.Range(0, 2).Select(_ => Glx.CreatePbuffer(_x11.Display, _fbconfig, new[]
             {
@@ -81,13 +83,14 @@ namespace Avalonia.X11.Glx
             
             XLib.XFlush(_x11.Display);
 
-            DeferredContext = CreateContext(CreatePBuffer(), null, true);
+            DeferredContext = CreateContext(CreatePBuffer(), null,
+                sampleCount, stencilSize, true);
             using (DeferredContext.MakeCurrent())
             {
-                GlInterface = new GlInterface(GlxInterface.SafeGetProcAddress);
-                if (GlInterface.Version == null)
+                var glInterface = new GlInterface(GlxInterface.SafeGetProcAddress);
+                if (glInterface.Version == null)
                     throw new OpenGlException("GL version string is null, aborting");
-                if (GlInterface.Renderer == null)
+                if (glInterface.Renderer == null)
                     throw new OpenGlException("GL renderer string is null, aborting");
 
                 if (Environment.GetEnvironmentVariable("AVALONIA_GLX_IGNORE_RENDERER_BLACKLIST") != "1")
@@ -96,9 +99,9 @@ namespace Avalonia.X11.Glx
                         ?.GlxRendererBlacklist;
                     if (blacklist != null)
                         foreach (var item in blacklist)
-                            if (GlInterface.Renderer.Contains(item))
+                            if (glInterface.Renderer.Contains(item))
                                 throw new OpenGlException(
-                                    $"Renderer '{GlInterface.Renderer}' is blacklisted by '{item}'");
+                                    $"Renderer '{glInterface.Renderer}' is blacklisted by '{item}'");
                 }
             }
 
@@ -111,40 +114,72 @@ namespace Avalonia.X11.Glx
 
 
         public GlxContext CreateContext() => CreateContext(DeferredContext);
-        GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share, true);
-        GlxContext CreateContext(IntPtr defaultXid, IGlContext share, bool ownsPBuffer)
+
+        GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
+            share.SampleCount, share.StencilSize, true);
+        
+        GlxContext CreateContext(IntPtr defaultXid, IGlContext share,
+            int sampleCount, int stencilSize, bool ownsPBuffer)
         {
             var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero;
             IntPtr handle = default;
-            foreach (var ver in new[]
-            {
-                new Version(4, 0), new Version(3, 2),
-                new Version(3, 0), new Version(2, 0)
-            })
+            
+            GlxContext Create(GlVersion profile)
             {
+                var profileMask = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
+                if (profile.Type == GlProfileType.OpenGLES) 
+                    profileMask = GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
 
-                var attrs = new[]
+                var attrs = new int[]
                 {
-                    GLX_CONTEXT_MAJOR_VERSION_ARB, ver.Major,
-                    GLX_CONTEXT_MINOR_VERSION_ARB, ver.Minor,
-                    GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+                    GLX_CONTEXT_MAJOR_VERSION_ARB, profile.Major,
+                    GLX_CONTEXT_MINOR_VERSION_ARB, profile.Minor,
+                    GLX_CONTEXT_PROFILE_MASK_ARB, profileMask,
                     0
                 };
+                
                 try
                 {
                     handle = Glx.CreateContextAttribsARB(_x11.Display, _fbconfig, sharelist, true, attrs);
                     if (handle != IntPtr.Zero)
-                        break;
+                    {
+                        _version = profile;
+                        return new GlxContext(new GlxInterface(), handle, this, profile,
+                            sampleCount, stencilSize, _x11, defaultXid, ownsPBuffer);
+                        
+                    }
                 }
                 catch
                 {
-                    break;
+                    return null;
                 }
+
+                return null;
+            }
+
+            GlxContext rv = null;
+            if (_version.HasValue)
+            {
+                rv = Create(_version.Value);
             }
             
-            if (handle == IntPtr.Zero)
-                throw new OpenGlException("Unable to create direct GLX context");
-            return new GlxContext(new GlxInterface(), handle, this, _x11, defaultXid, ownsPBuffer);
+            foreach (var v in _probeProfiles)
+            {
+                if (v.Type == GlProfileType.OpenGLES
+                    && !_displayExtensions.Contains("GLX_EXT_create_context_es2_profile"))
+                    continue;
+                rv = Create(v);
+                if (rv != null)
+                {
+                    _version = v;
+                    break;
+                }
+            }
+
+            if (rv != null)
+                return rv;
+
+            throw new OpenGlException("Unable to create direct GLX context");
         }
 
         public void SwapBuffers(IntPtr xid) => Glx.SwapBuffers(_x11.Display, xid);

+ 2 - 2
src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs

@@ -59,6 +59,7 @@ namespace Avalonia.X11.Glx
                 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)
@@ -71,7 +72,7 @@ namespace Avalonia.X11.Glx
 
                 public void Dispose()
                 {
-                    _context.Display.GlInterface.Flush();
+                    _context.GlInterface.Flush();
                     _context.Glx.WaitGL();
                     _context.Display.SwapBuffers(_info.Handle);
                     _context.Glx.WaitX();
@@ -79,7 +80,6 @@ namespace Avalonia.X11.Glx
                     _lock.Dispose();
                 }
 
-                public IGlDisplay Display => _context.Display;
                 public PixelSize Size => _info.Size;
                 public double Scaling => _info.Scaling;
                 public bool IsYFlipped { get; }

+ 6 - 6
src/Avalonia.X11/Glx/GlxPlatformFeature.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using Avalonia.Logging;
 using Avalonia.OpenGL;
 
@@ -8,13 +9,12 @@ namespace Avalonia.X11.Glx
     {
         public GlxDisplay Display { get; private set; }
         public IGlContext CreateContext() => Display.CreateContext();
-
-        IGlDisplay IWindowingPlatformGlFeature.Display => Display;
         public GlxContext DeferredContext { get; private set; }
+        public IGlContext MainContext => DeferredContext;
 
-        public static bool TryInitialize(X11Info x11)
+        public static bool TryInitialize(X11Info x11, List<GlVersion> glProfiles)
         {
-            var feature = TryCreate(x11);
+            var feature = TryCreate(x11, glProfiles);
             if (feature != null)
             {
                 AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature);
@@ -24,11 +24,11 @@ namespace Avalonia.X11.Glx
             return false;
         }
         
-        public static GlxGlPlatformFeature TryCreate(X11Info x11)
+        public static GlxGlPlatformFeature TryCreate(X11Info x11, List<GlVersion> glProfiles)
         {
             try
             {
-                var disp = new GlxDisplay(x11);
+                var disp = new GlxDisplay(x11, glProfiles);
                 return new GlxGlPlatformFeature
                 {
                     Display = disp,

+ 12 - 1
src/Avalonia.X11/X11Platform.cs

@@ -67,7 +67,7 @@ namespace Avalonia.X11
                 if (options.UseEGL)
                     EglGlPlatformFeature.TryInitialize();
                 else
-                    GlxGlPlatformFeature.TryInitialize(Info);
+                    GlxGlPlatformFeature.TryInitialize(Info, Options.GlProfiles);
             }
 
             
@@ -97,6 +97,17 @@ namespace Avalonia
         public bool OverlayPopups { get; set; }
         public bool UseDBusMenu { get; set; }
 
+        public List<GlVersion> GlProfiles { get; set; } = new List<GlVersion>
+        {
+            new GlVersion(GlProfileType.OpenGL, 4, 0),
+            new GlVersion(GlProfileType.OpenGL, 3, 2),
+            new GlVersion(GlProfileType.OpenGL, 3, 0),
+            new GlVersion(GlProfileType.OpenGLES, 3, 2),
+            new GlVersion(GlProfileType.OpenGLES, 3, 0),
+            new GlVersion(GlProfileType.OpenGLES, 2, 0),
+            new GlVersion(GlProfileType.OpenGL, 2, 0)
+        };
+
         public List<string> GlxRendererBlacklist { get; set; } = new List<string>
         {
             // llvmpipe is a software GL rasterizer. If it's returned by glGetString,

+ 1 - 1
src/Avalonia.X11/X11Window.cs

@@ -165,7 +165,7 @@ namespace Avalonia.X11
             
             if (egl != null)
                 surfaces.Insert(0,
-                    new EglGlPlatformSurface((EglDisplay)egl.Display, egl.DeferredContext,
+                    new EglGlPlatformSurface(egl.DeferredContext,
                         new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
             if (glx != null)
                 surfaces.Insert(0, new GlxGlPlatformSurface(glx.Display, glx.DeferredContext,

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

@@ -15,6 +15,8 @@ namespace Avalonia.LinuxFramebuffer.Output
         private readonly EglGlPlatformSurface _eglPlatformSurface;
         public PixelSize PixelSize => _mode.Resolution;
         public double Scaling { get; set; }
+        public IGlContext MainContext => _deferredContext;
+
         public DrmOutput(string path = null)
         {
             var card = new DrmCard(path);
@@ -132,8 +134,8 @@ namespace Avalonia.LinuxFramebuffer.Output
 
             using (_deferredContext.MakeCurrent(_eglSurface))
             {
-                _eglDisplay.GlInterface.ClearColor(0, 0, 0, 0);
-                _eglDisplay.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT);
+                _deferredContext.GlInterface.ClearColor(0, 0, 0, 0);
+                _deferredContext.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT);
                 _eglSurface.SwapBuffers();
             }
 
@@ -154,8 +156,8 @@ namespace Avalonia.LinuxFramebuffer.Output
             for(var c=0;c<2;c++)
                 using (CreateGlRenderTarget().BeginDraw())
                 {
-                    _eglDisplay.GlInterface.ClearColor(0, 0, 0, 0);
-                    _eglDisplay.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT);
+                    _deferredContext.GlInterface.ClearColor(0, 0, 0, 0);
+                    _deferredContext.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT);
                 }
             
         }
@@ -191,7 +193,7 @@ namespace Avalonia.LinuxFramebuffer.Output
 
                 public void Dispose()
                 {
-                    _parent._eglDisplay.GlInterface.Flush();
+                    _parent._deferredContext.GlInterface.Flush();
                     _parent._eglSurface.SwapBuffers();
                     
                     var nextBo = gbm_surface_lock_front_buffer(_parent._gbmTargetSurface);
@@ -233,7 +235,7 @@ namespace Avalonia.LinuxFramebuffer.Output
                 }
 
 
-                public IGlDisplay Display => _parent._eglDisplay;
+                public IGlContext Context => _parent._deferredContext;
 
                 public PixelSize Size => _parent._mode.Resolution;
 
@@ -248,7 +250,6 @@ namespace Avalonia.LinuxFramebuffer.Output
             }
         }
 
-        public IGlDisplay Display => _eglDisplay;
         public IGlContext CreateContext()
         {
             throw new NotImplementedException();

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

@@ -59,7 +59,7 @@ namespace Avalonia.Skia
             bool success = false;
             try
             {
-                var disp = glSession.Display;
+                var disp = glSession.Context;
                 var gl = disp.GlInterface;
                 gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var fb);
 

+ 5 - 7
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs

@@ -11,14 +11,12 @@ namespace Avalonia.Skia
 
         public GlSkiaGpu(IWindowingPlatformGlFeature gl)
         {
-            
-            var immediateContext = gl.CreateContext();
-            using (immediateContext.MakeCurrent())
+            var context = gl.MainContext;
+            using (context.MakeCurrent())
             {
-                var display = gl.Display;
-                using (var iface = display.Type == GlDisplayType.OpenGl ?
-                    GRGlInterface.AssembleGlInterface((_, proc) => display.GlInterface.GetProcAddress(proc)) :
-                    GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc)))
+                using (var iface = context.Version.Type == GlProfileType.OpenGL ?
+                    GRGlInterface.AssembleGlInterface((_, proc) => context.GlInterface.GetProcAddress(proc)) :
+                    GRGlInterface.AssembleGlesInterface((_, proc) => context.GlInterface.GetProcAddress(proc)))
                 {
                     _grContext = GRContext.Create(GRBackend.OpenGL, iface);
                 }

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

@@ -67,8 +67,7 @@ namespace Avalonia.Win32
             CreateWindow();
             _framebuffer = new FramebufferManager(_hwnd);
             if (Win32GlManager.EglFeature != null)
-                _gl = new EglGlPlatformSurface((EglDisplay)Win32GlManager.EglFeature.Display,
-                    Win32GlManager.EglFeature.DeferredContext, this);
+                _gl = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this);
 
             s_instances.Add(this);
         }