Browse Source

[GLX] Use glxGetProcAddress and always pass at least something to GlxMakeContextCurrent

pull/2265/head
Nikita Tsukanov 7 years ago
parent
commit
aa18bbf224
  1. 1
      src/Avalonia.OpenGL/GlEntryPointAttribute.cs
  2. 21
      src/Avalonia.OpenGL/GlInterface.cs
  3. 25
      src/Avalonia.OpenGL/GlInterfaceBase.cs
  4. 85
      src/Avalonia.X11/Glx/Glx.cs
  5. 17
      src/Avalonia.X11/Glx/GlxContext.cs
  6. 46
      src/Avalonia.X11/Glx/GlxDisplay.cs
  7. 4
      src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs

1
src/Avalonia.OpenGL/GlEntryPointAttribute.cs

@ -2,6 +2,7 @@ using System;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL
{ {
[AttributeUsage(AttributeTargets.Property)]
public class GlEntryPointAttribute : Attribute public class GlEntryPointAttribute : Attribute
{ {
public string EntryPoint { get; } public string EntryPoint { get; }

21
src/Avalonia.OpenGL/GlInterface.cs

@ -8,31 +8,24 @@ namespace Avalonia.OpenGL
public class GlInterface : GlInterfaceBase public class GlInterface : GlInterfaceBase
{ {
private readonly Func<string, bool, IntPtr> _getProcAddress;
public string Version { get; } public string Version { get; }
public GlInterface(Func<string, bool, IntPtr> getProcAddress) : base(getProcAddress) public GlInterface(Func<string, bool, IntPtr> getProcAddress) : base(getProcAddress)
{ {
_getProcAddress = getProcAddress;
var versionPtr = GetString(GlConsts.GL_VERSION); var versionPtr = GetString(GlConsts.GL_VERSION);
if (versionPtr != IntPtr.Zero) if (versionPtr != IntPtr.Zero)
Version = Marshal.PtrToStringAnsi(versionPtr); Version = Marshal.PtrToStringAnsi(versionPtr);
} }
public static GlInterface FromNativeUtf8GetProcAddress(Func<Utf8Buffer, IntPtr> getProcAddress) => public GlInterface(Func<Utf8Buffer, IntPtr> n) : this(ConvertNative(n))
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 IntPtr GetProcAddress(string proc) => _getProcAddress(proc, true); public static GlInterface FromNativeUtf8GetProcAddress(Func<Utf8Buffer, IntPtr> getProcAddress) =>
new GlInterface(getProcAddress);
public T GetProcAddress<T>(string proc) => Marshal.GetDelegateForFunctionPointer<T>(GetProcAddress(proc)); public T GetProcAddress<T>(string proc) => Marshal.GetDelegateForFunctionPointer<T>(GetProcAddress(proc));
// ReSharper disable UnassignedGetOnlyAutoProperty // ReSharper disable UnassignedGetOnlyAutoProperty

25
src/Avalonia.OpenGL/GlInterfaceBase.cs

@ -1,13 +1,17 @@
using System; using System;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Platform.Interop;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL
{ {
public class GlInterfaceBase public class GlInterfaceBase
{ {
private readonly Func<string, bool, IntPtr> _getProcAddress;
public GlInterfaceBase(Func<string, bool, IntPtr> getProcAddress) public GlInterfaceBase(Func<string, bool, IntPtr> getProcAddress)
{ {
_getProcAddress = getProcAddress;
foreach (var prop in this.GetType().GetProperties()) foreach (var prop in this.GetType().GetProperties())
{ {
var a = prop.GetCustomAttribute<GlEntryPointAttribute>(); var a = prop.GetCustomAttribute<GlEntryPointAttribute>();
@ -24,5 +28,26 @@ namespace Avalonia.OpenGL
} }
} }
} }
protected static Func<string, bool, IntPtr> ConvertNative(Func<Utf8Buffer, IntPtr> 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<Utf8Buffer, IntPtr> nativeGetProcAddress) : this(ConvertNative(nativeGetProcAddress))
{
}
public IntPtr GetProcAddress(string proc) => _getProcAddress(proc, true);
public IntPtr GetProcAddress(string proc, bool optional) => _getProcAddress(proc, optional);
} }
} }

85
src/Avalonia.X11/Glx/Glx.cs

@ -2,57 +2,90 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.OpenGL;
using Avalonia.Platform.Interop; using Avalonia.Platform.Interop;
// ReSharper disable UnassignedGetOnlyAutoProperty
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
static unsafe class Glx unsafe class GlxInterface : GlInterfaceBase
{ {
private const string libGL = "libGL.so.1"; private const string libGL = "libGL.so.1";
[DllImport(libGL, EntryPoint = "glXMakeCurrent")] [GlEntryPointAttribute("glXMakeContextCurrent")]
public static extern bool GlxMakeCurrent(IntPtr display, IntPtr drawable, IntPtr context); 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")] [GlEntryPointAttribute("glXCreateContext")]
public static extern IntPtr GlxCreateContext(IntPtr dpy, XVisualInfo* vis, IntPtr shareList, bool direct); public GlxCreateContext CreateContext { get; }
public delegate IntPtr GlxCreateContext(IntPtr dpy, XVisualInfo* vis, IntPtr shareList, bool direct);
[DllImport(libGL, EntryPoint = "glXCreateContextAttribsARB")] [GlEntryPointAttribute("glXCreateContextAttribsARB")]
public static extern IntPtr GlxCreateContextAttribsARB(IntPtr dpy, IntPtr fbconfig, IntPtr shareList, public GlxCreateContextAttribsARB CreateContextAttribsARB { get; }
public delegate IntPtr GlxCreateContextAttribsARB(IntPtr dpy, IntPtr fbconfig, IntPtr shareList,
bool direct, int[] attribs); bool direct, int[] attribs);
[DllImport(libGL, EntryPoint = "glXGetProcAddress")] [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<int> attribs, out int nelements) public IntPtr* GlxChooseFbConfig(IntPtr dpy, int screen, IEnumerable<int> attribs, out int nelements)
{ {
var arr = attribs.Concat(new[]{0}).ToArray(); 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")] [GlEntryPointAttribute("glXGetFBConfigAttrib")]
public static extern int GlxGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value); public GlxGetFBConfigAttrib GetFBConfigAttrib { get; }
public delegate int GlxGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value);
[DllImport(libGL, EntryPoint = "glXSwapBuffers")] [GlEntryPointAttribute("glXSwapBuffers")]
public static extern void GlxSwapBuffers(IntPtr dpy, IntPtr drawable); 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")] [GlEntryPointAttribute("glXWaitX")]
public static extern void GlxWaitGL(); 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)
{
}
} }
} }

17
src/Avalonia.X11/Glx/GlxContext.cs

@ -2,19 +2,22 @@ using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Threading; using System.Threading;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using static Avalonia.X11.Glx.Glx;
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
class GlxContext : IGlContext class GlxContext : IGlContext
{ {
public IntPtr Handle { get; } public IntPtr Handle { get; }
public GlxInterface Glx { get; }
private readonly X11Info _x11; private readonly X11Info _x11;
private readonly IntPtr _defaultXid;
private readonly object _lock = new object(); 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; Handle = handle;
Glx = glx;
_x11 = x11; _x11 = x11;
_defaultXid = defaultXid;
Display = display; Display = display;
} }
@ -27,8 +30,12 @@ namespace Avalonia.X11.Glx
return Disposable.Create(() => Monitor.Exit(_lock)); return Disposable.Create(() => Monitor.Exit(_lock));
} }
public void MakeCurrent() => MakeCurrent(IntPtr.Zero); public void MakeCurrent() => MakeCurrent(_defaultXid);
public void MakeCurrent(IntPtr xid) => GlxMakeCurrent(_x11.Display, xid, Handle); public void MakeCurrent(IntPtr xid)
{
if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle))
throw new OpenGlException("glXMakeContextCurrent failed ");
}
} }
} }

46
src/Avalonia.X11/Glx/GlxDisplay.cs

@ -2,7 +2,7 @@ using System;
using System.Linq; using System.Linq;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using static Avalonia.X11.Glx.GlxConsts; using static Avalonia.X11.Glx.GlxConsts;
using static Avalonia.X11.Glx.Glx;
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
unsafe class GlxDisplay : IGlDisplay unsafe class GlxDisplay : IGlDisplay
@ -19,7 +19,7 @@ namespace Avalonia.X11.Glx
public GlxContext ImmediateContext { get; } public GlxContext ImmediateContext { get; }
public GlxContext DeferredContext { get; } public GlxContext DeferredContext { get; }
public GlxInterface Glx { get; } = new GlxInterface();
public GlxDisplay(X11Info x11) public GlxDisplay(X11Info x11)
{ {
_x11 = x11; _x11 = x11;
@ -28,7 +28,7 @@ namespace Avalonia.X11.Glx
{ {
GLX_X_RENDERABLE, 1, GLX_X_RENDERABLE, 1,
GLX_RENDER_TYPE, GLX_RGBA_BIT, 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_DOUBLEBUFFER, 1,
GLX_RED_SIZE, 8, GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8, GLX_GREEN_SIZE, 8,
@ -45,12 +45,12 @@ namespace Avalonia.X11.Glx
baseAttribs, baseAttribs,
}) })
{ {
var ptr = GlxChooseFBConfig(_x11.Display, x11.DefaultScreen, var ptr = Glx.ChooseFBConfig(_x11.Display, x11.DefaultScreen,
attribs, out var count); attribs, out var count);
for (var c = 0 ; c < count; c++) 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 // We prefer 32 bit visuals
if (_fbconfig == IntPtr.Zero || visual->depth == 32) if (_fbconfig == IntPtr.Zero || visual->depth == 32)
{ {
@ -70,23 +70,33 @@ namespace Avalonia.X11.Glx
if (_visual == null) if (_visual == null)
throw new OpenGlException("Unable to get visual info from FBConfig"); 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; 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; StencilSize = stencil;
ImmediateContext = CreateContext(null); var pbuffers = Enumerable.Range(0, 2).Select(_ => Glx.CreatePbuffer(_x11.Display, _fbconfig, new[]
DeferredContext = CreateContext(ImmediateContext); {
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(); ImmediateContext.MakeCurrent();
var err = Glx.GetError();
GlInterface = GlInterface.FromNativeUtf8GetProcAddress(p => GlxGetProcAddress(p));
GlInterface = new GlInterface(GlxInterface.GlxGetProcAddress);
if (GlInterface.Version == null) if (GlInterface.Version == null)
throw new OpenGlException("GL version string is null, aborting"); 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; var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero;
IntPtr handle = default; IntPtr handle = default;
@ -106,7 +116,7 @@ namespace Avalonia.X11.Glx
}; };
try try
{ {
handle = GlxCreateContextAttribsARB(_x11.Display, _fbconfig, sharelist, true, attrs); handle = Glx.CreateContextAttribsARB(_x11.Display, _fbconfig, sharelist, true, attrs);
if (handle != IntPtr.Zero) if (handle != IntPtr.Zero)
break; break;
} }
@ -116,13 +126,11 @@ namespace Avalonia.X11.Glx
} }
} }
if(handle == IntPtr.Zero)
handle = GlxCreateContext(_x11.Display, _visual, sharelist, true);
if (handle == IntPtr.Zero) if (handle == IntPtr.Zero)
throw new OpenGlException("Unable to create direct GLX context"); 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);
} }
} }

4
src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs

@ -70,9 +70,9 @@ namespace Avalonia.X11.Glx
public void Dispose() public void Dispose()
{ {
_context.Display.GlInterface.Flush(); _context.Display.GlInterface.Flush();
Glx.GlxWaitGL(); _context.Glx.WaitGL();
_context.Display.SwapBuffers(_info.Handle); _context.Display.SwapBuffers(_info.Handle);
Glx.GlxWaitX(); _context.Glx.WaitX();
_context.Display.ClearContext(); _context.Display.ClearContext();
_lock.Dispose(); _lock.Dispose();
} }

Loading…
Cancel
Save