Browse Source

[X11] Use GLX and transparent visuals when possible

pull/2255/head
Nikita Tsukanov 7 years ago
parent
commit
ed9dff6b8f
  1. 12
      src/Avalonia.OpenGL/EglDisplay.cs
  2. 28
      src/Avalonia.OpenGL/EglInterface.cs
  3. 4
      src/Avalonia.OpenGL/GlEntryPointAttribute.cs
  4. 35
      src/Avalonia.OpenGL/GlInterface.cs
  5. 2
      src/Avalonia.OpenGL/GlInterfaceBase.cs
  6. 55
      src/Avalonia.X11/Glx/Glx.cs
  7. 97
      src/Avalonia.X11/Glx/GlxConsts.cs
  8. 34
      src/Avalonia.X11/Glx/GlxContext.cs
  9. 98
      src/Avalonia.X11/Glx/GlxDisplay.cs
  10. 86
      src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
  11. 44
      src/Avalonia.X11/Glx/GlxPlatformFeature.cs
  12. 6
      src/Avalonia.X11/X11FramebufferSurface.cs
  13. 8
      src/Avalonia.X11/X11Info.cs
  14. 24
      src/Avalonia.X11/X11Platform.cs
  15. 75
      src/Avalonia.X11/X11Window.cs
  16. 3
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

12
src/Avalonia.OpenGL/EglDisplay.cs

@ -86,18 +86,8 @@ namespace Avalonia.OpenGL
if (_contextAttributes == null)
throw new OpenGlException("No suitable EGL config was found");
GlInterface = new GlInterface((proc, optional) =>
{
using (var u = new Utf8Buffer(proc))
{
var rv = _egl.GetProcAddress(u);
if (rv == IntPtr.Zero && !optional)
throw new OpenGlException("Missing function " + proc);
return rv;
}
});
GlInterface = GlInterface.FromNativeUtf8GetProcAddress(b => _egl.GetProcAddress(b));
}
public EglDisplay() : this(new EglInterface())

28
src/Avalonia.OpenGL/EglInterface.cs

@ -34,61 +34,61 @@ namespace Avalonia.OpenGL
// ReSharper disable UnassignedGetOnlyAutoProperty
public delegate int EglGetError();
[EntryPoint("eglGetError")]
[GlEntryPoint("eglGetError")]
public EglGetError GetError { get; }
public delegate IntPtr EglGetDisplay(IntPtr nativeDisplay);
[EntryPoint("eglGetDisplay")]
[GlEntryPoint("eglGetDisplay")]
public EglGetDisplay GetDisplay { get; }
public delegate IntPtr EglGetPlatformDisplayEXT(int platform, IntPtr nativeDisplay, int[] attrs);
[EntryPoint("eglGetPlatformDisplayEXT", true)]
[GlEntryPoint("eglGetPlatformDisplayEXT", true)]
public EglGetPlatformDisplayEXT GetPlatformDisplayEXT { get; }
public delegate bool EglInitialize(IntPtr display, out int major, out int minor);
[EntryPoint("eglInitialize")]
[GlEntryPoint("eglInitialize")]
public EglInitialize Initialize { get; }
public delegate IntPtr EglGetProcAddress(Utf8Buffer proc);
[EntryPoint("eglGetProcAddress")]
[GlEntryPoint("eglGetProcAddress")]
public EglGetProcAddress GetProcAddress { get; }
public delegate bool EglBindApi(int api);
[EntryPoint("eglBindAPI")]
[GlEntryPoint("eglBindAPI")]
public EglBindApi BindApi { get; }
public delegate bool EglChooseConfig(IntPtr display, int[] attribs,
out IntPtr surfaceConfig, int numConfigs, out int choosenConfig);
[EntryPoint("eglChooseConfig")]
[GlEntryPoint("eglChooseConfig")]
public EglChooseConfig ChooseConfig { get; }
public delegate IntPtr EglCreateContext(IntPtr display, IntPtr config,
IntPtr share, int[] attrs);
[EntryPoint("eglCreateContext")]
[GlEntryPoint("eglCreateContext")]
public EglCreateContext CreateContext { get; }
public delegate IntPtr EglCreatePBufferSurface(IntPtr display, IntPtr config, int[] attrs);
[EntryPoint("eglCreatePbufferSurface")]
[GlEntryPoint("eglCreatePbufferSurface")]
public EglCreatePBufferSurface CreatePBufferSurface { get; }
public delegate bool EglMakeCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context);
[EntryPoint("eglMakeCurrent")]
[GlEntryPoint("eglMakeCurrent")]
public EglMakeCurrent MakeCurrent { get; }
public delegate void EglDisplaySurfaceVoidDelegate(IntPtr display, IntPtr surface);
[EntryPoint("eglDestroySurface")]
[GlEntryPoint("eglDestroySurface")]
public EglDisplaySurfaceVoidDelegate DestroySurface { get; }
[EntryPoint("eglSwapBuffers")]
[GlEntryPoint("eglSwapBuffers")]
public EglDisplaySurfaceVoidDelegate SwapBuffers { get; }
public delegate IntPtr
EglCreateWindowSurface(IntPtr display, IntPtr config, IntPtr window, int[] attrs);
[EntryPoint("eglCreateWindowSurface")]
[GlEntryPoint("eglCreateWindowSurface")]
public EglCreateWindowSurface CreateWindowSurface { get; }
public delegate bool EglGetConfigAttrib(IntPtr display, IntPtr config, int attr, out int rv);
[EntryPoint("eglGetConfigAttrib")]
[GlEntryPoint("eglGetConfigAttrib")]
public EglGetConfigAttrib GetConfigAttrib { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty

4
src/Avalonia.OpenGL/EntryPointAttribute.cs → src/Avalonia.OpenGL/GlEntryPointAttribute.cs

@ -2,12 +2,12 @@ using System;
namespace Avalonia.OpenGL
{
class EntryPointAttribute : Attribute
public class GlEntryPointAttribute : Attribute
{
public string EntryPoint { get; }
public bool Optional { get; }
public EntryPointAttribute(string entryPoint, bool optional = false)
public GlEntryPointAttribute(string entryPoint, bool optional = false)
{
EntryPoint = entryPoint;
Optional = optional;

35
src/Avalonia.OpenGL/GlInterface.cs

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Platform.Interop;
namespace Avalonia.OpenGL
{
@ -8,42 +9,62 @@ 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 IntPtr GetProcAddress(string proc) => _getProcAddress(proc, true);
public T GetProcAddress<T>(string proc) => Marshal.GetDelegateForFunctionPointer<T>(GetProcAddress(proc));
// ReSharper disable UnassignedGetOnlyAutoProperty
public delegate int GlGetError();
[EntryPoint("glGetError")]
[GlEntryPoint("glGetError")]
public GlGetError GetError { get; }
public delegate void GlClearStencil(int s);
[EntryPoint("glClearStencil")]
[GlEntryPoint("glClearStencil")]
public GlClearStencil ClearStencil { get; }
public delegate void GlClearColor(int r, int g, int b, int a);
[EntryPoint("glClearColor")]
[GlEntryPoint("glClearColor")]
public GlClearColor ClearColor { get; }
public delegate void GlClear(int bits);
[EntryPoint("glClear")]
[GlEntryPoint("glClear")]
public GlClear Clear { get; }
public delegate void GlViewport(int x, int y, int width, int height);
[EntryPoint("glViewport")]
[GlEntryPoint("glViewport")]
public GlViewport Viewport { get; }
[EntryPoint("glFlush")]
[GlEntryPoint("glFlush")]
public Action Flush { get; }
public delegate IntPtr GlGetString(int v);
[GlEntryPoint("glGetString")]
public GlGetString GetString { get; }
public delegate void GlGetIntegerv(int name, out int rv);
[EntryPoint("glGetIntegerv")]
[GlEntryPoint("glGetIntegerv")]
public GlGetIntegerv GetIntegerv { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty

2
src/Avalonia.OpenGL/GlInterfaceBase.cs

@ -10,7 +10,7 @@ namespace Avalonia.OpenGL
{
foreach (var prop in this.GetType().GetProperties())
{
var a = prop.GetCustomAttribute<EntryPointAttribute>();
var a = prop.GetCustomAttribute<GlEntryPointAttribute>();
if (a != null)
{
var fieldName = $"<{prop.Name}>k__BackingField";

55
src/Avalonia.X11/Glx/Glx.cs

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

97
src/Avalonia.X11/Glx/GlxConsts.cs

@ -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;
}
}

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

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

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

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

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

@ -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;
}
}
}
}

44
src/Avalonia.X11/Glx/GlxPlatformFeature.cs

@ -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;
}
}
}
}

6
src/Avalonia.X11/X11FramebufferSurface.cs

@ -8,12 +8,14 @@ namespace Avalonia.X11
{
private readonly IntPtr _display;
private readonly IntPtr _xid;
private readonly int _depth;
private readonly Func<double> _scaling;
public X11FramebufferSurface(IntPtr display, IntPtr xid, Func<double> scaling)
public X11FramebufferSurface(IntPtr display, IntPtr xid, int depth, Func<double> scaling)
{
_display = display;
_xid = xid;
_depth = depth;
_scaling = scaling;
}
@ -23,7 +25,7 @@ namespace Avalonia.X11
XGetGeometry(_display, _xid, out var root, out var x, out var y, out var width, out var height,
out var bw, out var d);
XUnlockDisplay(_display);
return new X11Framebuffer(_display, _xid, 24,width, height, _scaling());
return new X11Framebuffer(_display, _xid, _depth, width, height, _scaling());
}
}
}

8
src/Avalonia.X11/X11Info.cs

@ -7,7 +7,7 @@ using static Avalonia.X11.XLib;
// ReSharper disable UnusedAutoPropertyAccessor.Local
namespace Avalonia.X11
{
class X11Info
unsafe class X11Info
{
public IntPtr Display { get; }
public IntPtr DeferredDisplay { get; }
@ -31,6 +31,7 @@ namespace Avalonia.X11
public Version XInputVersion { get; }
public IntPtr LastActivityTimestamp { get; set; }
public XVisualInfo? TransparentVisualInfo { get; set; }
public unsafe X11Info(IntPtr display, IntPtr deferredDisplay)
{
@ -45,7 +46,10 @@ namespace Avalonia.X11
//TODO: Open an actual XIM once we get support for preedit in our textbox
XSetLocaleModifiers("@im=none");
Xim = XOpenIM(display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
XMatchVisualInfo(Display, DefaultScreen, 32, 4, out var visual);
if (visual.depth == 32)
TransparentVisualInfo = visual;
try
{
if (XRRQueryExtension(display, out int randrEventBase, out var randrErrorBase) != 0)

24
src/Avalonia.X11/X11Platform.cs

@ -9,6 +9,7 @@ using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.X11;
using Avalonia.X11.Glx;
using static Avalonia.X11.XLib;
namespace Avalonia.X11
{
@ -23,7 +24,7 @@ namespace Avalonia.X11
public X11Info Info { get; private set; }
public IX11Screens X11Screens { get; private set; }
public IScreenImpl Screens { get; private set; }
public void Initialize()
public void Initialize(X11PlatformOptions options)
{
XInitThreads();
Display = XOpenDisplay(IntPtr.Zero);
@ -54,8 +55,14 @@ namespace Avalonia.X11
if (xi2.Init(this))
XI2 = xi2;
}
EglGlPlatformFeature.TryInitialize();
if (options.UseGpu)
{
if (options.UseEGL)
EglGlPlatformFeature.TryInitialize();
else
GlxGlPlatformFeature.TryInitialize(Info);
}
}
public IntPtr DeferredDisplay { get; set; }
@ -79,15 +86,22 @@ namespace Avalonia.X11
namespace Avalonia
{
public class X11PlatformOptions
{
public bool UseEGL { get; set; }
public bool UseGpu { get; set; } = true;
}
public static class AvaloniaX11PlatformExtensions
{
public static T UseX11<T>(this T builder) where T : AppBuilderBase<T>, new()
public static T UseX11<T>(this T builder, X11PlatformOptions options = null) where T : AppBuilderBase<T>, new()
{
builder.UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize());
builder.UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize(options ?? new X11PlatformOptions()));
return builder;
}
public static void InitializeX11Platform() => new AvaloniaX11Platform().Initialize();
public static void InitializeX11Platform(X11PlatformOptions options = null) =>
new AvaloniaX11Platform().Initialize(options ?? new X11PlatformOptions());
}
}

75
src/Avalonia.X11/X11Window.cs

@ -12,6 +12,7 @@ using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
using Avalonia.X11.Glx;
using static Avalonia.X11.XLib;
// ReSharper disable IdentifierTypo
// ReSharper disable StringLiteralTypo
@ -45,7 +46,7 @@ namespace Avalonia.X11
}
private readonly Queue<InputEventContainer> _inputQueue = new Queue<InputEventContainer>();
private InputEventContainer _lastEvent;
private bool _useRenderWindow = false;
public X11Window(AvaloniaX11Platform platform, bool popup)
{
_platform = platform;
@ -55,7 +56,7 @@ namespace Avalonia.X11
_keyboard = platform.KeyboardDevice;
_xic = XCreateIC(_x11.Xim, XNames.XNInputStyle, XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing,
XNames.XNClientWindow, _handle, IntPtr.Zero);
var glfeature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
XSetWindowAttributes attr = new XSetWindowAttributes();
var valueMask = default(SetWindowValuemask);
@ -71,16 +72,44 @@ namespace Avalonia.X11
attr.override_redirect = true;
valueMask |= SetWindowValuemask.OverrideRedirect;
}
XVisualInfo? visualInfo = null;
var glx = glfeature as GlxGlPlatformFeature;
if (glx != null)
visualInfo = *glx.Display.VisualInfo;
else if (glfeature == null)
visualInfo = _x11.TransparentVisualInfo;
var egl = glfeature as EglGlPlatformFeature;
if (egl != null)
_useRenderWindow = true;
var visual = IntPtr.Zero;
var depth = 24;
if (visualInfo != null)
{
visual = visualInfo.Value.visual;
depth = (int)visualInfo.Value.depth;
attr.colormap = XCreateColormap(_x11.Display, _x11.RootWindow, visualInfo.Value.visual, 0);
valueMask |= SetWindowValuemask.ColorMap;
}
_handle = XCreateWindow(_x11.Display, _x11.RootWindow, 10, 10, 300, 200, 0,
24,
(int)CreateWindowArgs.InputOutput, IntPtr.Zero,
depth,
(int)CreateWindowArgs.InputOutput,
visual,
new UIntPtr((uint)valueMask), ref attr);
_renderHandle = XCreateWindow(_x11.Display, _handle, 0, 0, 300, 200, 0, 24,
(int)CreateWindowArgs.InputOutput,
IntPtr.Zero,
new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity |
SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
if (_useRenderWindow)
_renderHandle = XCreateWindow(_x11.Display, _handle, 0, 0, 300, 200, 0, depth,
(int)CreateWindowArgs.InputOutput,
visual,
new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity |
SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
else
_renderHandle = _handle;
Handle = new PlatformHandle(_handle, "XID");
_realSize = new PixelSize(300, 200);
@ -99,16 +128,22 @@ namespace Avalonia.X11
XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length);
XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM,
32, PropertyMode.Replace, new[] {_x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL}, 1);
var feature = (EglGlPlatformFeature)AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
var surfaces = new List<object>
{
new X11FramebufferSurface(_x11.DeferredDisplay, _renderHandle, () => Scaling)
new X11FramebufferSurface(_x11.DeferredDisplay, _renderHandle,
depth, () => Scaling)
};
if (feature != null)
if (egl != null)
surfaces.Insert(0,
new EglGlPlatformSurface((EglDisplay)feature.Display, feature.DeferredContext,
new EglGlPlatformSurface((EglDisplay)egl.Display, egl.DeferredContext,
new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
if (glx != null)
surfaces.Insert(0, new GlxGlPlatformSurface(glx.Display, glx.DeferredContext,
new SurfaceInfo(this, _x11.Display, _handle, _renderHandle)));
Surfaces = surfaces.ToArray();
UpdateMotifHints();
XFlush(_x11.Display);
@ -252,7 +287,8 @@ namespace Avalonia.X11
if (ev.type == XEventName.MapNotify)
{
_mapped = true;
XMapWindow(_x11.Display, _renderHandle);
if (_useRenderWindow)
XMapWindow(_x11.Display, _renderHandle);
}
else if (ev.type == XEventName.UnmapNotify)
_mapped = false;
@ -356,7 +392,9 @@ namespace Avalonia.X11
Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout);
}, DispatcherPriority.Layout);
XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width, ev.ConfigureEvent.height);
if (_useRenderWindow)
XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width,
ev.ConfigureEvent.height);
}
else if (ev.type == XEventName.DestroyNotify && ev.AnyEvent.window == _handle)
{
@ -619,7 +657,7 @@ namespace Avalonia.X11
Closed?.Invoke();
}
if (_renderHandle != IntPtr.Zero)
if (_useRenderWindow && _renderHandle != IntPtr.Zero)
{
XDestroyWindow(_x11.Display, _renderHandle);
_renderHandle = IntPtr.Zero;
@ -687,7 +725,8 @@ namespace Avalonia.X11
var pixelSize = ToPixelSize(clientSize);
UpdateSizeHints(pixelSize);
XConfigureResizeWindow(_x11.Display, _handle, pixelSize);
XConfigureResizeWindow(_x11.Display, _renderHandle, pixelSize);
if (_useRenderWindow)
XConfigureResizeWindow(_x11.Display, _renderHandle, pixelSize);
XFlush(_x11.Display);
if (force || (_popup && needImmediatePopupResize))

3
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -27,11 +27,12 @@ namespace Avalonia.Skia
if (gl != null)
{
var display = gl.ImmediateContext.Display;
gl.ImmediateContext.MakeCurrent();
using (var iface = display.Type == GlDisplayType.OpenGL2
? GRGlInterface.AssembleGlInterface((_, proc) => display.GlInterface.GetProcAddress(proc))
: GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc)))
{
gl.ImmediateContext.MakeCurrent();
GrContext = GRContext.Create(GRBackend.OpenGL, iface);
}
}

Loading…
Cancel
Save