diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 854cae484c..c78487bb28 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/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 + { + new GlVersion(GlProfileType.OpenGLES, 2, 0) + } }) .With(new Win32PlatformOptions { diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs index 12a3c17fe3..1698b69678 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs +++ b/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"); diff --git a/src/Avalonia.Native/GlPlatformFeature.cs b/src/Avalonia.Native/GlPlatformFeature.cs index 83c741658a..131fb2d987 100644 --- a/src/Avalonia.Native/GlPlatformFeature.cs +++ b/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(); - 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); } } diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs index 9423b2aa2a..edc989a953 100644 --- a/src/Avalonia.Native/PopupImpl.cs +++ b/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)); } diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index c757576017..2e9f5b079b 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/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); diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 331f083e8b..d2984a6bcd 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/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(); } - 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; diff --git a/src/Avalonia.OpenGL/EglContext.cs b/src/Avalonia.OpenGL/EglContext.cs index 8c3a00c308..d05bd081c8 100644 --- a/src/Avalonia.OpenGL/EglContext.cs +++ b/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); diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs index 8a81b2b584..0436f6ac52 100644 --- a/src/Avalonia.OpenGL/EglDisplay.cs +++ b/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; - } - } } } diff --git a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs index ff7df67921..cf3bce8756 100644 --- a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs +++ b/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() { diff --git a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs index 3bbf5c6a70..3e4befe2c6 100644 --- a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs +++ b/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; } diff --git a/src/Avalonia.OpenGL/EglSurface.cs b/src/Avalonia.OpenGL/EglSurface.cs index 90bb1e36d0..5ac56a00e3 100644 --- a/src/Avalonia.OpenGL/EglSurface.cs +++ b/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); } -} \ No newline at end of file +} diff --git a/src/Avalonia.OpenGL/GlDisplayType.cs b/src/Avalonia.OpenGL/GlDisplayType.cs deleted file mode 100644 index 6ce7600ec6..0000000000 --- a/src/Avalonia.OpenGL/GlDisplayType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Avalonia.OpenGL -{ - public enum GlDisplayType - { - OpenGl, - OpenGLES - } -} diff --git a/src/Avalonia.OpenGL/GlVersion.cs b/src/Avalonia.OpenGL/GlVersion.cs new file mode 100644 index 0000000000..042ff4c2f0 --- /dev/null +++ b/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; + } + } +} diff --git a/src/Avalonia.OpenGL/IGlContext.cs b/src/Avalonia.OpenGL/IGlContext.cs index d5201c7a50..eb4313fba9 100644 --- a/src/Avalonia.OpenGL/IGlContext.cs +++ b/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(); } } diff --git a/src/Avalonia.OpenGL/IGlDisplay.cs b/src/Avalonia.OpenGL/IGlDisplay.cs deleted file mode 100644 index ec1435a7ba..0000000000 --- a/src/Avalonia.OpenGL/IGlDisplay.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Avalonia.OpenGL -{ - public interface IGlDisplay - { - GlDisplayType Type { get; } - GlInterface GlInterface { get; } - int SampleCount { get; } - int StencilSize { get; } - } -} diff --git a/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs b/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs index f56f47095e..89911a20a8 100644 --- a/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs +++ b/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; } diff --git a/src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs b/src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs index 8a24820704..b91496f42b 100644 --- a/src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs +++ b/src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs @@ -2,7 +2,7 @@ namespace Avalonia.OpenGL { public interface IWindowingPlatformGlFeature { - IGlDisplay Display { get; } IGlContext CreateContext(); + IGlContext MainContext { get; } } } diff --git a/src/Avalonia.OpenGL/OpenGlControlBase.cs b/src/Avalonia.OpenGL/OpenGlControlBase.cs index e133938701..6268c81516 100644 --- a/src/Avalonia.OpenGL/OpenGlControlBase.cs +++ b/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); diff --git a/src/Avalonia.X11/Glx/Glx.cs b/src/Avalonia.X11/Glx/Glx.cs index d14d52b819..d3c09f4702 100644 --- a/src/Avalonia.X11/Glx/Glx.cs +++ b/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 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(); + + } } } diff --git a/src/Avalonia.X11/Glx/GlxConsts.cs b/src/Avalonia.X11/Glx/GlxConsts.cs index 65f253696a..9d153a6ade 100644 --- a/src/Avalonia.X11/Glx/GlxConsts.cs +++ b/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; } diff --git a/src/Avalonia.X11/Glx/GlxContext.cs b/src/Avalonia.X11/Glx/GlxContext.cs index 7770cc4976..ca6d3aa657 100644 --- a/src/Avalonia.X11/Glx/GlxContext.cs +++ b/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) diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs index 2522ff35ab..26fb22de63 100644 --- a/src/Avalonia.X11/Glx/GlxDisplay.cs +++ b/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 _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 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); diff --git a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs index b05930afc9..ae6b0eb353 100644 --- a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs +++ b/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; } diff --git a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs index 0665977681..046036fd68 100644 --- a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs +++ b/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 glProfiles) { - var feature = TryCreate(x11); + var feature = TryCreate(x11, glProfiles); if (feature != null) { AvaloniaLocator.CurrentMutable.Bind().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 glProfiles) { try { - var disp = new GlxDisplay(x11); + var disp = new GlxDisplay(x11, glProfiles); return new GlxGlPlatformFeature { Display = disp, diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 8b531bd9c5..59b9a8dae6 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/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 GlProfiles { get; set; } = new List + { + 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 GlxRendererBlacklist { get; set; } = new List { // llvmpipe is a software GL rasterizer. If it's returned by glGetString, diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 94f1dbc8d1..fc9a8dadba 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/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, diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs index b64da5a0b9..0f5b66befb 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs +++ b/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(); diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs index e3aff53901..9b73174006 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs +++ b/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); diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs index 76c149e53b..be0311c23a 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs +++ b/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); } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index cea9f88799..f45ed9f885 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/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); }