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
{
[AttributeUsage(AttributeTargets.Property)]
public class GlEntryPointAttribute : Attribute
{
public string EntryPoint { get; }

21
src/Avalonia.OpenGL/GlInterface.cs

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

25
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<string, bool, IntPtr> _getProcAddress;
public GlInterfaceBase(Func<string, bool, IntPtr> getProcAddress)
{
_getProcAddress = getProcAddress;
foreach (var prop in this.GetType().GetProperties())
{
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.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<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();
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)
{
}
}
}

17
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 ");
}
}
}

46
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);
}
}

4
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();
}

Loading…
Cancel
Save