From aa18bbf22475f7731f322c3f7befe27f3cddfe85 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 23 Jan 2019 17:06:36 +0300 Subject: [PATCH] [GLX] Use glxGetProcAddress and always pass at least something to GlxMakeContextCurrent --- src/Avalonia.OpenGL/GlEntryPointAttribute.cs | 1 + src/Avalonia.OpenGL/GlInterface.cs | 21 ++--- src/Avalonia.OpenGL/GlInterfaceBase.cs | 25 ++++++ src/Avalonia.X11/Glx/Glx.cs | 85 ++++++++++++++------ src/Avalonia.X11/Glx/GlxContext.cs | 17 ++-- src/Avalonia.X11/Glx/GlxDisplay.cs | 46 ++++++----- src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs | 4 +- 7 files changed, 133 insertions(+), 66 deletions(-) diff --git a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs index fbde8e57be..016c3d0af9 100644 --- a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs +++ b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs @@ -2,6 +2,7 @@ using System; namespace Avalonia.OpenGL { + [AttributeUsage(AttributeTargets.Property)] public class GlEntryPointAttribute : Attribute { public string EntryPoint { get; } diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs index 7921989c04..718afc4a94 100644 --- a/src/Avalonia.OpenGL/GlInterface.cs +++ b/src/Avalonia.OpenGL/GlInterface.cs @@ -8,31 +8,24 @@ namespace Avalonia.OpenGL public class GlInterface : GlInterfaceBase { - private readonly Func _getProcAddress; public string Version { get; } public GlInterface(Func getProcAddress) : base(getProcAddress) { - _getProcAddress = getProcAddress; var versionPtr = GetString(GlConsts.GL_VERSION); if (versionPtr != IntPtr.Zero) Version = Marshal.PtrToStringAnsi(versionPtr); } - public static GlInterface FromNativeUtf8GetProcAddress(Func getProcAddress) => - new GlInterface((proc, optional) => - { - using (var u = new Utf8Buffer(proc)) - { - var rv = getProcAddress(u); - if (rv == IntPtr.Zero && !optional) - throw new OpenGlException("Missing function " + proc); - return rv; - } - }); + public GlInterface(Func n) : this(ConvertNative(n)) + { + + } - public IntPtr GetProcAddress(string proc) => _getProcAddress(proc, true); + public static GlInterface FromNativeUtf8GetProcAddress(Func getProcAddress) => + new GlInterface(getProcAddress); + public T GetProcAddress(string proc) => Marshal.GetDelegateForFunctionPointer(GetProcAddress(proc)); // ReSharper disable UnassignedGetOnlyAutoProperty diff --git a/src/Avalonia.OpenGL/GlInterfaceBase.cs b/src/Avalonia.OpenGL/GlInterfaceBase.cs index 8b3eb2797d..89ec0e77d4 100644 --- a/src/Avalonia.OpenGL/GlInterfaceBase.cs +++ b/src/Avalonia.OpenGL/GlInterfaceBase.cs @@ -1,13 +1,17 @@ using System; using System.Reflection; using System.Runtime.InteropServices; +using Avalonia.Platform.Interop; namespace Avalonia.OpenGL { public class GlInterfaceBase { + + private readonly Func _getProcAddress; public GlInterfaceBase(Func getProcAddress) { + _getProcAddress = getProcAddress; foreach (var prop in this.GetType().GetProperties()) { var a = prop.GetCustomAttribute(); @@ -24,5 +28,26 @@ namespace Avalonia.OpenGL } } } + + protected static Func ConvertNative(Func func) => + (proc, optional) => + { + using (var u = new Utf8Buffer(proc)) + { + var rv = func(u); + if (rv == IntPtr.Zero && !optional) + throw new OpenGlException("Missing function " + proc); + return rv; + } + }; + + public GlInterfaceBase(Func nativeGetProcAddress) : this(ConvertNative(nativeGetProcAddress)) + { + + } + + public IntPtr GetProcAddress(string proc) => _getProcAddress(proc, true); + public IntPtr GetProcAddress(string proc, bool optional) => _getProcAddress(proc, optional); + } } diff --git a/src/Avalonia.X11/Glx/Glx.cs b/src/Avalonia.X11/Glx/Glx.cs index de558e8e6a..c3a2fd2050 100644 --- a/src/Avalonia.X11/Glx/Glx.cs +++ b/src/Avalonia.X11/Glx/Glx.cs @@ -2,57 +2,90 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using Avalonia.OpenGL; using Avalonia.Platform.Interop; +// ReSharper disable UnassignedGetOnlyAutoProperty namespace Avalonia.X11.Glx { - static unsafe class Glx + unsafe class GlxInterface : GlInterfaceBase { private const string libGL = "libGL.so.1"; - [DllImport(libGL, EntryPoint = "glXMakeCurrent")] - public static extern bool GlxMakeCurrent(IntPtr display, IntPtr drawable, IntPtr context); + [GlEntryPointAttribute("glXMakeContextCurrent")] + public GlxMakeContextCurrent MakeContextCurrent { get; } + public delegate bool GlxMakeContextCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context); + [GlEntryPoint("glXCreatePbuffer")] + public GlxCreatePbuffer CreatePbuffer { get; } + + public delegate IntPtr GlxCreatePbuffer(IntPtr dpy, IntPtr fbc, int[] attrib_list); + + [GlEntryPointAttribute("glXChooseVisual")] + public GlxChooseVisual ChooseVisual { get; } + public delegate XVisualInfo* GlxChooseVisual(IntPtr dpy, int screen, int[] attribList); - [DllImport(libGL, EntryPoint = "glXChooseVisual")] - public static extern XVisualInfo* GlxChooseVisual(IntPtr dpy, int screen, int[] attribList); - [DllImport(libGL, EntryPoint = "glXCreateContext")] - public static extern IntPtr GlxCreateContext(IntPtr dpy, XVisualInfo* vis, IntPtr shareList, bool direct); + [GlEntryPointAttribute("glXCreateContext")] + public GlxCreateContext CreateContext { get; } + public delegate IntPtr GlxCreateContext(IntPtr dpy, XVisualInfo* vis, IntPtr shareList, bool direct); + - [DllImport(libGL, EntryPoint = "glXCreateContextAttribsARB")] - public static extern IntPtr GlxCreateContextAttribsARB(IntPtr dpy, IntPtr fbconfig, IntPtr shareList, + [GlEntryPointAttribute("glXCreateContextAttribsARB")] + public GlxCreateContextAttribsARB CreateContextAttribsARB { get; } + public delegate IntPtr GlxCreateContextAttribsARB(IntPtr dpy, IntPtr fbconfig, IntPtr shareList, bool direct, int[] attribs); + [DllImport(libGL, EntryPoint = "glXGetProcAddress")] - public static extern IntPtr GlxGetProcAddress(Utf8Buffer buffer); + public static extern IntPtr GlxGetProcAddress(Utf8Buffer buffer); + + + [GlEntryPointAttribute("glXDestroyContext")] + public GlxDestroyContext DestroyContext { get; } + public delegate void GlxDestroyContext(IntPtr dpy, IntPtr ctx); - [DllImport(libGL, EntryPoint = "glXDestroyContext")] - public static extern void GlxDestroyContext(IntPtr dpy, IntPtr ctx); + [GlEntryPointAttribute("glXChooseFBConfig")] + public GlxChooseFBConfig ChooseFBConfig { get; } + public delegate IntPtr* GlxChooseFBConfig(IntPtr dpy, int screen, int[] attrib_list, out int nelements); - [DllImport(libGL, EntryPoint = "glXChooseFBConfig")] - public static extern IntPtr* GlxChooseFBConfig(IntPtr dpy, int screen, int[] attrib_list, out int nelements); - public static IntPtr* GlxChooseFbConfig(IntPtr dpy, int screen, IEnumerable attribs, out int nelements) + public IntPtr* GlxChooseFbConfig(IntPtr dpy, int screen, IEnumerable attribs, out int nelements) { var arr = attribs.Concat(new[]{0}).ToArray(); - return GlxChooseFBConfig(dpy, screen, arr, out nelements); + return ChooseFBConfig(dpy, screen, arr, out nelements); } - [DllImport(libGL, EntryPoint = "glXGetVisualFromFBConfig")] - public static extern XVisualInfo * GlxGetVisualFromFBConfig(IntPtr dpy, IntPtr config); + + [GlEntryPointAttribute("glXGetVisualFromFBConfig")] + public GlxGetVisualFromFBConfig GetVisualFromFBConfig { get; } + public delegate XVisualInfo * GlxGetVisualFromFBConfig(IntPtr dpy, IntPtr config); - [DllImport(libGL, EntryPoint = "glXGetFBConfigAttrib")] - public static extern int GlxGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value); + [GlEntryPointAttribute("glXGetFBConfigAttrib")] + public GlxGetFBConfigAttrib GetFBConfigAttrib { get; } + public delegate int GlxGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value); - [DllImport(libGL, EntryPoint = "glXSwapBuffers")] - public static extern void GlxSwapBuffers(IntPtr dpy, IntPtr drawable); + [GlEntryPointAttribute("glXSwapBuffers")] + public GlxSwapBuffers SwapBuffers { get; } + public delegate void GlxSwapBuffers(IntPtr dpy, IntPtr drawable); - [DllImport(libGL, EntryPoint = "glXWaitX")] - public static extern void GlxWaitX(); - [DllImport(libGL, EntryPoint = "glXWaitGL")] - public static extern void GlxWaitGL(); + [GlEntryPointAttribute("glXWaitX")] + public GlxWaitX WaitX { get; } + public delegate void GlxWaitX(); + + + [GlEntryPointAttribute("glXWaitGL")] + public GlxWaitGL WaitGL { get; } + public delegate void GlxWaitGL(); + + public delegate int GlGetError(); + [GlEntryPoint("glGetError")] + public GlGetError GetError { get; } + + public GlxInterface() : base(GlxGetProcAddress) + { + } } } diff --git a/src/Avalonia.X11/Glx/GlxContext.cs b/src/Avalonia.X11/Glx/GlxContext.cs index b263d77666..dd95841d57 100644 --- a/src/Avalonia.X11/Glx/GlxContext.cs +++ b/src/Avalonia.X11/Glx/GlxContext.cs @@ -2,19 +2,22 @@ using System; using System.Reactive.Disposables; using System.Threading; using Avalonia.OpenGL; -using static Avalonia.X11.Glx.Glx; namespace Avalonia.X11.Glx { class GlxContext : IGlContext { public IntPtr Handle { get; } + public GlxInterface Glx { get; } private readonly X11Info _x11; + private readonly IntPtr _defaultXid; private readonly object _lock = new object(); - public GlxContext(IntPtr handle, GlxDisplay display, X11Info x11) + public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display, X11Info x11, IntPtr defaultXid) { Handle = handle; + Glx = glx; _x11 = x11; + _defaultXid = defaultXid; Display = display; } @@ -27,8 +30,12 @@ namespace Avalonia.X11.Glx return Disposable.Create(() => Monitor.Exit(_lock)); } - public void MakeCurrent() => MakeCurrent(IntPtr.Zero); - - public void MakeCurrent(IntPtr xid) => GlxMakeCurrent(_x11.Display, xid, Handle); + public void MakeCurrent() => MakeCurrent(_defaultXid); + + public void MakeCurrent(IntPtr xid) + { + if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle)) + throw new OpenGlException("glXMakeContextCurrent failed "); + } } } diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs index 7e6d2328e5..5602b33280 100644 --- a/src/Avalonia.X11/Glx/GlxDisplay.cs +++ b/src/Avalonia.X11/Glx/GlxDisplay.cs @@ -2,7 +2,7 @@ using System; using System.Linq; using Avalonia.OpenGL; using static Avalonia.X11.Glx.GlxConsts; -using static Avalonia.X11.Glx.Glx; + namespace Avalonia.X11.Glx { unsafe class GlxDisplay : IGlDisplay @@ -19,7 +19,7 @@ namespace Avalonia.X11.Glx public GlxContext ImmediateContext { get; } public GlxContext DeferredContext { get; } - + public GlxInterface Glx { get; } = new GlxInterface(); public GlxDisplay(X11Info x11) { _x11 = x11; @@ -28,7 +28,7 @@ namespace Avalonia.X11.Glx { GLX_X_RENDERABLE, 1, GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT, GLX_DOUBLEBUFFER, 1, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, @@ -45,12 +45,12 @@ namespace Avalonia.X11.Glx baseAttribs, }) { - var ptr = GlxChooseFBConfig(_x11.Display, x11.DefaultScreen, + var ptr = Glx.ChooseFBConfig(_x11.Display, x11.DefaultScreen, attribs, out var count); for (var c = 0 ; c < count; c++) { - var visual = GlxGetVisualFromFBConfig(_x11.Display, ptr[c]); + var visual = Glx.GetVisualFromFBConfig(_x11.Display, ptr[c]); // We prefer 32 bit visuals if (_fbconfig == IntPtr.Zero || visual->depth == 32) { @@ -70,23 +70,33 @@ namespace Avalonia.X11.Glx if (_visual == null) throw new OpenGlException("Unable to get visual info from FBConfig"); - if (GlxGetFBConfigAttrib(_x11.Display, _fbconfig, GLX_SAMPLES, out var samples) == 0) + if (Glx.GetFBConfigAttrib(_x11.Display, _fbconfig, GLX_SAMPLES, out var samples) == 0) SampleCount = samples; - if (GlxGetFBConfigAttrib(_x11.Display, _fbconfig, GLX_STENCIL_SIZE, out var stencil) == 0) + if (Glx.GetFBConfigAttrib(_x11.Display, _fbconfig, GLX_STENCIL_SIZE, out var stencil) == 0) StencilSize = stencil; - ImmediateContext = CreateContext(null); - DeferredContext = CreateContext(ImmediateContext); + var pbuffers = Enumerable.Range(0, 2).Select(_ => Glx.CreatePbuffer(_x11.Display, _fbconfig, new[] + { + GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 + })).ToList(); + + XLib.XFlush(_x11.Display); + + ImmediateContext = CreateContext(pbuffers[0],null); + DeferredContext = CreateContext(pbuffers[1], ImmediateContext); ImmediateContext.MakeCurrent(); - - GlInterface = GlInterface.FromNativeUtf8GetProcAddress(p => GlxGetProcAddress(p)); + var err = Glx.GetError(); + + GlInterface = new GlInterface(GlxInterface.GlxGetProcAddress); if (GlInterface.Version == null) throw new OpenGlException("GL version string is null, aborting"); } - - public void ClearContext() => GlxMakeCurrent(_x11.Display, IntPtr.Zero, IntPtr.Zero); - public GlxContext CreateContext(IGlContext share) + public void ClearContext() => Glx.MakeContextCurrent(_x11.Display, + IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + + public GlxContext CreateContext(IGlContext share) => CreateContext(IntPtr.Zero, share); + public GlxContext CreateContext(IntPtr defaultXid, IGlContext share) { var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero; IntPtr handle = default; @@ -106,7 +116,7 @@ namespace Avalonia.X11.Glx }; try { - handle = GlxCreateContextAttribsARB(_x11.Display, _fbconfig, sharelist, true, attrs); + handle = Glx.CreateContextAttribsARB(_x11.Display, _fbconfig, sharelist, true, attrs); if (handle != IntPtr.Zero) break; } @@ -116,13 +126,11 @@ namespace Avalonia.X11.Glx } } - if(handle == IntPtr.Zero) - handle = GlxCreateContext(_x11.Display, _visual, sharelist, true); if (handle == IntPtr.Zero) throw new OpenGlException("Unable to create direct GLX context"); - return new GlxContext(handle, this, _x11); + return new GlxContext(new GlxInterface(), handle, this, _x11, defaultXid); } - public void SwapBuffers(IntPtr xid) => GlxSwapBuffers(_x11.Display, xid); + 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 f6a4090dc0..45a46bd6f5 100644 --- a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs +++ b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs @@ -70,9 +70,9 @@ namespace Avalonia.X11.Glx public void Dispose() { _context.Display.GlInterface.Flush(); - Glx.GlxWaitGL(); + _context.Glx.WaitGL(); _context.Display.SwapBuffers(_info.Handle); - Glx.GlxWaitX(); + _context.Glx.WaitX(); _context.Display.ClearContext(); _lock.Dispose(); }