16 changed files with 548 additions and 63 deletions
@ -0,0 +1,55 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.Platform.Interop; |
|||
|
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
static unsafe class Glx |
|||
{ |
|||
private const string libGL = "libGL.so.1"; |
|||
[DllImport(libGL, EntryPoint = "glXMakeCurrent")] |
|||
public static extern bool GlxMakeCurrent(IntPtr display, IntPtr drawable, IntPtr context); |
|||
|
|||
|
|||
[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); |
|||
|
|||
|
|||
[DllImport(libGL, EntryPoint = "glXGetProcAddress")] |
|||
public static extern IntPtr GlxGetProcAddress(Utf8Buffer buffer); |
|||
|
|||
[DllImport(libGL, EntryPoint = "glXDestroyContext")] |
|||
public static extern void GlxDestroyContext(IntPtr dpy, IntPtr ctx); |
|||
|
|||
|
|||
[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) |
|||
{ |
|||
var arr = attribs.Concat(new[]{0}).ToArray(); |
|||
return GlxChooseFBConfig(dpy, screen, arr, out nelements); |
|||
} |
|||
[DllImport(libGL, EntryPoint = "glXGetVisualFromFBConfig")] |
|||
public static extern XVisualInfo * GlxGetVisualFromFBConfig(IntPtr dpy, IntPtr config); |
|||
|
|||
|
|||
[DllImport(libGL, EntryPoint = "glXGetFBConfigAttrib")] |
|||
public static extern int GlxGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value); |
|||
|
|||
|
|||
[DllImport(libGL, EntryPoint = "glXSwapBuffers")] |
|||
public static extern void GlxSwapBuffers(IntPtr dpy, IntPtr drawable); |
|||
|
|||
[DllImport(libGL, EntryPoint = "glXWaitX")] |
|||
public static extern void GlxWaitX(); |
|||
|
|||
[DllImport(libGL, EntryPoint = "glXWaitGL")] |
|||
public static extern void GlxWaitGL(); |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
// ReSharper disable IdentifierTypo
|
|||
// ReSharper disable UnusedMember.Global
|
|||
#pragma warning disable 414
|
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
class GlxConsts |
|||
{ |
|||
public const int GLX_USE_GL = 1; |
|||
public const int GLX_BUFFER_SIZE = 2; |
|||
public const int GLX_LEVEL = 3; |
|||
public const int GLX_RGBA = 4; |
|||
public const int GLX_DOUBLEBUFFER = 5; |
|||
public const int GLX_STEREO = 6; |
|||
public const int GLX_AUX_BUFFERS = 7; |
|||
public const int GLX_RED_SIZE = 8; |
|||
public const int GLX_GREEN_SIZE = 9; |
|||
public const int GLX_BLUE_SIZE = 10; |
|||
public const int GLX_ALPHA_SIZE = 11; |
|||
public const int GLX_DEPTH_SIZE = 12; |
|||
public const int GLX_STENCIL_SIZE = 13; |
|||
public const int GLX_ACCUM_RED_SIZE = 14; |
|||
public const int GLX_ACCUM_GREEN_SIZE = 15; |
|||
public const int GLX_ACCUM_BLUE_SIZE = 16; |
|||
public const int GLX_ACCUM_ALPHA_SIZE = 17; |
|||
public const int GLX_BAD_SCREEN = 1; |
|||
public const int GLX_BAD_ATTRIBUTE = 2; |
|||
public const int GLX_NO_EXTENSION = 3; |
|||
public const int GLX_BAD_VISUAL = 4; |
|||
public const int GLX_BAD_CONTEXT = 5; |
|||
public const int GLX_BAD_VALUE = 6; |
|||
public const int GLX_BAD_ENUM = 7; |
|||
public const int GLX_VENDOR = 1; |
|||
public const int GLX_VERSION = 2; |
|||
public const int GLX_EXTENSIONS= 3; |
|||
public const int GLX_CONFIG_CAVEAT = 0x20; |
|||
public const int GLX_DONT_CARE = unchecked((int)0xFFFFFFFF); |
|||
public const int GLX_X_VISUAL_TYPE = 0x22; |
|||
public const int GLX_TRANSPARENT_TYPE = 0x23; |
|||
public const int GLX_TRANSPARENT_INDEX_VALUE = 0x24; |
|||
public const int GLX_TRANSPARENT_RED_VALUE = 0x25; |
|||
public const int GLX_TRANSPARENT_GREEN_VALUE = 0x26; |
|||
public const int GLX_TRANSPARENT_BLUE_VALUE = 0x27; |
|||
public const int GLX_TRANSPARENT_ALPHA_VALUE = 0x28; |
|||
public const int GLX_WINDOW_BIT = 0x00000001; |
|||
public const int GLX_PIXMAP_BIT = 0x00000002; |
|||
public const int GLX_PBUFFER_BIT = 0x00000004; |
|||
public const int GLX_AUX_BUFFERS_BIT = 0x00000010; |
|||
public const int GLX_FRONT_LEFT_BUFFER_BIT = 0x00000001; |
|||
public const int GLX_FRONT_RIGHT_BUFFER_BIT = 0x00000002; |
|||
public const int GLX_BACK_LEFT_BUFFER_BIT = 0x00000004; |
|||
public const int GLX_BACK_RIGHT_BUFFER_BIT = 0x00000008; |
|||
public const int GLX_DEPTH_BUFFER_BIT = 0x00000020; |
|||
public const int GLX_STENCIL_BUFFER_BIT = 0x00000040; |
|||
public const int GLX_ACCUM_BUFFER_BIT = 0x00000080; |
|||
public const int GLX_NONE = 0x8000; |
|||
public const int GLX_SLOW_CONFIG = 0x8001; |
|||
public const int GLX_TRUE_COLOR = 0x8002; |
|||
public const int GLX_DIRECT_COLOR = 0x8003; |
|||
public const int GLX_PSEUDO_COLOR = 0x8004; |
|||
public const int GLX_STATIC_COLOR = 0x8005; |
|||
public const int GLX_GRAY_SCALE = 0x8006; |
|||
public const int GLX_STATIC_GRAY = 0x8007; |
|||
public const int GLX_TRANSPARENT_RGB = 0x8008; |
|||
public const int GLX_TRANSPARENT_INDEX = 0x8009; |
|||
public const int GLX_VISUAL_ID = 0x800B; |
|||
public const int GLX_SCREEN = 0x800C; |
|||
public const int GLX_NON_CONFORMANT_CONFIG = 0x800D; |
|||
public const int GLX_DRAWABLE_TYPE = 0x8010; |
|||
public const int GLX_RENDER_TYPE = 0x8011; |
|||
public const int GLX_X_RENDERABLE = 0x8012; |
|||
public const int GLX_FBCONFIG_ID = 0x8013; |
|||
public const int GLX_RGBA_TYPE = 0x8014; |
|||
public const int GLX_COLOR_INDEX_TYPE = 0x8015; |
|||
public const int GLX_MAX_PBUFFER_WIDTH = 0x8016; |
|||
public const int GLX_MAX_PBUFFER_HEIGHT = 0x8017; |
|||
public const int GLX_MAX_PBUFFER_PIXELS = 0x8018; |
|||
public const int GLX_PRESERVED_CONTENTS = 0x801B; |
|||
public const int GLX_LARGEST_PBUFFER = 0x801C; |
|||
public const int GLX_WIDTH = 0x801D; |
|||
public const int GLX_HEIGHT = 0x801E; |
|||
public const int GLX_EVENT_MASK = 0x801F; |
|||
public const int GLX_DAMAGED = 0x8020; |
|||
public const int GLX_SAVED = 0x8021; |
|||
public const int GLX_WINDOW = 0x8022; |
|||
public const int GLX_PBUFFER = 0x8023; |
|||
public const int GLX_PBUFFER_HEIGHT = 0x8040; |
|||
public const int GLX_PBUFFER_WIDTH = 0x8041; |
|||
public const int GLX_RGBA_BIT = 0x00000001; |
|||
public const int GLX_COLOR_INDEX_BIT = 0x00000002; |
|||
public const int GLX_PBUFFER_CLOBBER_MASK = 0x08000000; |
|||
public const int GLX_SAMPLE_BUFFERS = 0x186a0 /*100000*/; |
|||
public const int GLX_SAMPLES = 0x186a1 /*100001*/; |
|||
public const int GLX_PbufferClobber = 0; |
|||
public const int GLX_BufferSwapComplete = 1; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
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; } |
|||
private readonly X11Info _x11; |
|||
private readonly object _lock = new object(); |
|||
|
|||
public GlxContext(IntPtr handle, GlxDisplay display, X11Info x11) |
|||
{ |
|||
Handle = handle; |
|||
_x11 = x11; |
|||
Display = display; |
|||
} |
|||
|
|||
public GlxDisplay Display { get; } |
|||
IGlDisplay IGlContext.Display => Display; |
|||
|
|||
public IDisposable Lock() |
|||
{ |
|||
Monitor.Enter(_lock); |
|||
return Disposable.Create(() => Monitor.Exit(_lock)); |
|||
} |
|||
|
|||
public void MakeCurrent() => MakeCurrent(IntPtr.Zero); |
|||
|
|||
public void MakeCurrent(IntPtr xid) => GlxMakeCurrent(_x11.Display, xid, Handle); |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
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 |
|||
{ |
|||
private readonly X11Info _x11; |
|||
private readonly IntPtr _fbconfig; |
|||
private readonly XVisualInfo* _visual; |
|||
public GlDisplayType Type => GlDisplayType.OpenGL2; |
|||
public GlInterface GlInterface { get; } |
|||
|
|||
public XVisualInfo* VisualInfo => _visual; |
|||
public int SampleCount { get; } |
|||
public int StencilSize { get; } |
|||
|
|||
public GlxContext ImmediateContext { get; } |
|||
public GlxContext DeferredContext { get; } |
|||
|
|||
public GlxDisplay(X11Info x11) |
|||
{ |
|||
_x11 = x11; |
|||
|
|||
var baseAttribs = new[] |
|||
{ |
|||
GLX_X_RENDERABLE, 1, |
|||
GLX_RENDER_TYPE, GLX_RGBA_BIT, |
|||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, |
|||
GLX_DOUBLEBUFFER, 1, |
|||
GLX_RED_SIZE, 8, |
|||
GLX_GREEN_SIZE, 8, |
|||
GLX_BLUE_SIZE, 8, |
|||
GLX_ALPHA_SIZE, 8, |
|||
GLX_DEPTH_SIZE, 1, |
|||
GLX_STENCIL_SIZE, 8, |
|||
|
|||
}; |
|||
|
|||
foreach (var attribs in new[] |
|||
{ |
|||
//baseAttribs.Concat(multiattribs),
|
|||
baseAttribs, |
|||
}) |
|||
{ |
|||
var ptr = GlxChooseFBConfig(_x11.Display, x11.DefaultScreen, |
|||
attribs, out var count); |
|||
for (var c = 0 ; c < count; c++) |
|||
{ |
|||
|
|||
var visual = GlxGetVisualFromFBConfig(_x11.Display, ptr[c]); |
|||
// We prefer 32 bit visuals
|
|||
if (_fbconfig == IntPtr.Zero || visual->depth == 32) |
|||
{ |
|||
_fbconfig = ptr[c]; |
|||
_visual = visual; |
|||
if(visual->depth == 32) |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (_fbconfig != IntPtr.Zero) |
|||
break; |
|||
} |
|||
|
|||
if (_fbconfig == IntPtr.Zero) |
|||
throw new OpenGlException("Unable to choose FBConfig"); |
|||
|
|||
if (_visual == null) |
|||
throw new OpenGlException("Unable to get visual info from FBConfig"); |
|||
if (GlxGetFBConfigAttrib(_x11.Display, _fbconfig, GLX_SAMPLES, out var samples) == 0) |
|||
SampleCount = samples; |
|||
if (GlxGetFBConfigAttrib(_x11.Display, _fbconfig, GLX_STENCIL_SIZE, out var stencil) == 0) |
|||
StencilSize = stencil; |
|||
|
|||
ImmediateContext = CreateContext(null); |
|||
DeferredContext = CreateContext(ImmediateContext); |
|||
ImmediateContext.MakeCurrent(); |
|||
|
|||
GlInterface = GlInterface.FromNativeUtf8GetProcAddress(p => GlxGetProcAddress(p)); |
|||
} |
|||
|
|||
public void ClearContext() => GlxMakeCurrent(_x11.Display, IntPtr.Zero, IntPtr.Zero); |
|||
|
|||
public GlxContext CreateContext(IGlContext share) |
|||
{ |
|||
var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero; |
|||
var h = GlxCreateContext(_x11.Display, _visual, sharelist, true); |
|||
if (h == IntPtr.Zero) |
|||
throw new OpenGlException("Unable to create direct GLX context"); |
|||
return new GlxContext(h, this, _x11); |
|||
} |
|||
|
|||
public void SwapBuffers(IntPtr xid) => GlxSwapBuffers(_x11.Display, xid); |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
using System; |
|||
using Avalonia.OpenGL; |
|||
|
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
class GlxGlPlatformSurface: IGlPlatformSurface |
|||
{ |
|||
|
|||
private readonly GlxDisplay _display; |
|||
private readonly GlxContext _context; |
|||
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; |
|||
|
|||
public GlxGlPlatformSurface(GlxDisplay display, GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) |
|||
{ |
|||
_display = display; |
|||
_context = context; |
|||
_info = info; |
|||
} |
|||
|
|||
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() |
|||
{ |
|||
return new RenderTarget(_context, _info); |
|||
} |
|||
|
|||
class RenderTarget : IGlPlatformSurfaceRenderTarget |
|||
{ |
|||
private readonly GlxContext _context; |
|||
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; |
|||
|
|||
public RenderTarget(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) |
|||
{ |
|||
_context = context; |
|||
_info = info; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
// No-op
|
|||
} |
|||
|
|||
public IGlPlatformSurfaceRenderingSession BeginDraw() |
|||
{ |
|||
var l = _context.Lock(); |
|||
try |
|||
{ |
|||
_context.MakeCurrent(_info.Handle); |
|||
return new Session(_context, _info, l); |
|||
} |
|||
catch |
|||
{ |
|||
l.Dispose(); |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
class Session : IGlPlatformSurfaceRenderingSession |
|||
{ |
|||
private readonly GlxContext _context; |
|||
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; |
|||
private IDisposable _lock; |
|||
|
|||
public Session(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info, |
|||
IDisposable @lock) |
|||
{ |
|||
_context = context; |
|||
_info = info; |
|||
_lock = @lock; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_context.Display.GlInterface.Flush(); |
|||
Glx.GlxWaitGL(); |
|||
_context.Display.SwapBuffers(_info.Handle); |
|||
Glx.GlxWaitX(); |
|||
_context.Display.ClearContext(); |
|||
_lock.Dispose(); |
|||
} |
|||
|
|||
public IGlDisplay Display => _context.Display; |
|||
public PixelSize Size => _info.Size; |
|||
public double Scaling => _info.Scaling; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.OpenGL; |
|||
|
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
class GlxGlPlatformFeature : IWindowingPlatformGlFeature |
|||
{ |
|||
public GlxDisplay Display { get; private set; } |
|||
public IGlContext ImmediateContext { get; private set; } |
|||
public GlxContext DeferredContext { get; private set; } |
|||
|
|||
public static bool TryInitialize(X11Info x11) |
|||
{ |
|||
var feature = TryCreate(x11); |
|||
if (feature != null) |
|||
{ |
|||
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public static GlxGlPlatformFeature TryCreate(X11Info x11) |
|||
{ |
|||
try |
|||
{ |
|||
var disp = new GlxDisplay(x11); |
|||
return new GlxGlPlatformFeature |
|||
{ |
|||
Display = disp, |
|||
ImmediateContext = disp.ImmediateContext, |
|||
DeferredContext = disp.DeferredContext |
|||
}; |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
Logger.Error("OpenGL", null, "Unable to initialize GLX-based rendering: {0}", e); |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue